我正在尝试了解Go语言。我试图创建两个goroutine,使用两个通道将它们之间的流链接起来:
func main() { c1 := make(chan int) c2 := make(chan int) go func() { for i := range c1{ println("G1 got", i) c2 <- i } }() go func() { for i := range c2 { println("G2 got", i) c1 <- i } }() c1 <- 1 time.Sleep(1000000000 * 50) }
如预期的那样,此代码将输出:
G1 got 1 G2 got 1 G1 got 1 G2 got 1 ....
直到主要功能退出。
但是,如果我将另一个值从main发送到其中一个通道,则会突然阻塞:
func main() { c1 := make(chan int) c2 := make(chan int) go func() { for i := range c1{ println("G1 got", i) c2 <- i } }() go func() { for i := range c2 { println("G2 got", i) c1 <- i } }() c1 <- 1 time.Sleep(1000000000 * 1) c1 <- 2 time.Sleep(1000000000 * 50) }
它输出
G1 got 1 G2 got 1 G1 got 1 G2 got 1 G1 got 2
然后阻塞,直到主体结束。
发送给c1的值“ 2”到达第一个goroutie,后者将其发送给c2,但是第二个goroutine从不接收。
(在此示例中,使用大小为1(c1或c2)的缓冲通道)
为什么会发生?当这种情况发生在真实代码中时,我该如何调试呢?
创建的Go通道make(chan int)未缓冲。如果您想要一个缓冲的通道(不一定会阻塞),请使用make(chan int, 2)2表示通道的大小。
make(chan int)
make(chan int, 2)
关于无缓冲通道的事情是它们也是同步的,因此它们总是在 写 和读时阻塞。
之所以陷入僵局,是因为您的第一个goroutine正在等待其c2 <- i完成,而第二个goroutine正在等待c1 <- i完成,因为中存在额外的内容c1。我发现在实际代码中调试此类事件的最佳方法是查看哪些goroutine被阻塞并认真思考。
c2 <- i
c1 <- i
c1
您也可以仅在确实需要同步通道时才使用同步通道来回避问题。