小编典典

进入频道并陷入僵局

go

我正在尝试了解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)的缓冲通道)

为什么会发生?当这种情况发生在真实代码中时,我该如何调试呢?


阅读 302

收藏
2020-07-02

共1个答案

小编典典

创建的Go通道make(chan int)未缓冲。如果您想要一个缓冲的通道(不一定会阻塞),请使用make(chan int, 2)2表示通道的大小。

关于无缓冲通道的事情是它们也是同步的,因此它们总是在 和读时阻塞。

之所以陷入僵局,是因为您的第一个goroutine正在等待其c2 <- i完成,而第二个goroutine正在等待c1 <- i完成,因为中存在额外的内容c1。我发现在实际代码中调试此类事件的最佳方法是查看哪些goroutine被阻塞并认真思考。

您也可以仅在确实需要同步通道时才使用同步通道来回避问题。

2020-07-02