GO channel
理念
GO语言并发模型CSP: 提倡通过通信共享内存,而非通过共享内存实现通信。
如果说goroutine
是Go程序并发的执行体,channel
就是它们之间的连接。channel
是可以让一个goroutine
发送特定值到另一个goroutine
的通信机制
示例
关闭后任然可以读取
func main() { ch := make(chan int, 2) ch <- 1 ch <- 2 close(ch) a, ok1 := <-ch b, ok2 := <-ch c, ok3 := <-ch fmt.Println(a, ok1) fmt.Println(b, ok2) fmt.Println(c, ok3) }
执行:
1 true
2 true
0 false
异常示例
# 无缓冲通道导致异常
import "fmt" func baseTest() { ch := make(chan int) ch <- 10 fmt.Println("发送成功") } func main() { baseTest() }
执行:fatal error: all goroutines are asleep - deadlock!
原因:ch := make(chan int) 创建的是无缓冲通道,只有在有地接收值时才会发送成功。
# 通道无空间,继续读取
func testClose() { ch := make(chan int, 2) ch <- 1 ch <- 2 //close(ch) a, ok1 := <-ch b, ok2 := <-ch c, ok3 := <-ch fmt.Println(a, ok1) fmt.Println(b, ok2) fmt.Println(c, ok3) }
执行:fatal error: all goroutines are asleep - deadlock!
# 关闭后,任往通道发送
func main() { ch := make(chan int, 2) ch <- 1 close(ch) ch <- 2 a, ok1 := <-ch b, ok2 := <-ch c, ok3 := <-ch fmt.Println(a, ok1) fmt.Println(b, ok2) fmt.Println(c, ok3) }
执行:
panic: send on closed channel
#解1:无缓冲通道
import "fmt" func recv(ch chan int) { ret := <- ch fmt.Println("接收成功: ", ret) } func unBuffer() { ch := make(chan int) go recv(ch) ch <- 10 fmt.Println("发送成功") } func main() { unBuffer() }
执行:
接收成功: 10
发送成功
#解2:有缓冲通道
import "fmt" func buffer() { ch := make(chan int, 2) ch <- 10 fmt.Println("发送成功") } func main() { buffer() }
执行:
发送成功
问题:当发送到chan大于其长度,并且没有消费时,造成死锁
import "fmt" func buffer() { ch := make(chan int, 2) ch <- 10 ch <- 11 ch <- 12 fmt.Println("发送成功") } func main() { buffer() }
执行:
fatal error: all goroutines are asleep - deadlock!
综合示例
读取对通道a中的数字,在通道b中平方
package main import ( "fmt" "sync" ) var wg2 sync.WaitGroup func f1(ch1 chan int) { defer wg2.Done() for i := 0; i < 100; i++ { ch1 <- i } close(ch1) } func f2(ch1, ch2 chan int) { defer wg2.Done() for { x, ok := <-ch1 if !ok { break } ch2 <- x * x } close(ch2) } func testClose() { ch := make(chan int, 2) ch <- 1 ch <- 2 close(ch) a, ok1 := <-ch b, ok2 := <-ch c, ok3 := <-ch fmt.Println(a, ok1) fmt.Println(b, ok2) fmt.Println(c, ok3) } func main() { //testClose() a := make(chan int, 100) b := make(chan int, 100) wg2.Add(2) go f1(a) go f2(a, b) wg2.Wait() for x := range b { fmt.Println(x) } }
执行:
0
1
4
9
16
...
单向通道
<-chan:只做输出的通道
chan<-:只做输入的通道
示例: 重写上面例子中f2
func f2Uni(ch1 <-chan int, ch2 chan<- int) { defer wg2.Done() for { x, ok := <-ch1 if !ok { break } ch2 <- x * x } close(ch2) }