17

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

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

4

3 回答 3

21

nmichaels 的回答是正确的,但我想我要补充一点,在调试这样的问题时,有一些方法可以找出你陷入僵局的地方。

一个简单的方法是,如果您使用的是类 Unix 操作系统,请运行命令

kill -6 [pid]

这将终止程序并为每个 goroutine 提供堆栈跟踪。

一个稍微复杂的方法是附加 gdb。

gdb [executable name] [pid]

您可以照常检查活动 goroutine 的堆栈和变量,但据我所知,没有简单的方法可以切换 goroutine。您可以以通常的方式切换操作系统线程,但这可能不足以提供帮助。

于 2011-05-13T04:30:52.733 回答
20

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

无缓冲通道的问题在于它们也是同步的,因此它们总是在写入和读取时阻塞。

它死锁的原因是你的第一个 goroutine 正在等待它c2 <- i完成,而第二个正在等待c1 <- i完成,因为c1. 当它发生在实际代码中时,我发现调试这类事情的最佳方法是查看哪些 goroutines 被阻塞并认真思考。

如果确实需要,您也可以通过仅使用同步通道来回避这个问题。

于 2011-05-12T19:43:02.737 回答
2

为防止通道溢出,您可以询问通道的当前容量并在再次写入之前将其擦干。

就我而言,游戏以 60fps 的速度进行,并且鼠标移动得更快,因此在再次写入之前检查通道是否已清除总是好的。

注意之前的数据丢失了

package main

import (
    "fmt"
)

func main() {
    // you must specify the size of the channel, 
    // even for just one element, or the code doesn't work
    ch := make( chan int, 1 )
    fmt.Printf("len: %v\n", len(ch))
    fmt.Printf("cap: %v\n\n", cap(ch))

    ch <- 1

    for i := 0; i != 100; i += 1 {
        fmt.Printf("len: %v\n", len(ch))
        fmt.Printf("cap: %v\n\n", cap(ch))

        if cap( ch ) == 1 {
            <- ch
        }

        ch <- i

        fmt.Printf("len: %v\n", len(ch))
        fmt.Printf("cap: %v\n\n", cap(ch))
    }
    fmt.Printf("end!\n")
}
于 2020-01-20T14:54:32.087 回答