我正在努力理解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)
}
Run Code Online (Sandbox Code Playgroud)
正如所料,此代码打印:
G1 got 1
G2 got 1
G1 got 1
G2 got 1
....
Run Code Online (Sandbox Code Playgroud)
直到主要功能退出.
但是如果我从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)
}
Run Code Online (Sandbox Code Playgroud)
它输出
G1 got 1
G2 got 1
G1 got 1
G2 got 1
G1 got 2
Run Code Online (Sandbox Code Playgroud)
然后阻塞直到主要结束.
发送到c1的值"2"到达第一个goroutie,后者将其发送到c2,但第二个goroutine从未接收到.
(在此示例中使用大小为1的缓冲通道(c1或c2))
为什么会这样?如果在实际代码中发生这种情况,我该如何调试呢?
Eva*_*haw 19
nmichaels对他的答案是正确的,但我想我会补充说,在调试这样的问题时,有办法找出你在哪里陷入僵局.
一个简单的问题是,如果您使用的是类Unix操作系统,请运行该命令
kill -6 [pid]
Run Code Online (Sandbox Code Playgroud)
这将终止程序并为每个goroutine提供堆栈跟踪.
一个稍微复杂一点的方法是附加gdb.
gdb [executable name] [pid]
Run Code Online (Sandbox Code Playgroud)
您可以正常检查活动goroutine的堆栈和变量,但是没有简单的方法来切换我所知道的goroutine.您可以通过常规方式切换操作系统线程,但这可能不足以提供帮助.
nmi*_*els 18
创建的Go通道make(chan int)
未缓冲.如果您想要一个缓冲的通道(不一定会阻塞),请使用make(chan int, 2)
其中2是通道大小.
关于无缓冲通道的事情是它们也是同步的,所以它们总是阻塞写入和读取.
它陷入僵局的原因是你的第一个goroutine正在等待它c2 <- i
完成,而第二个goroutine正在等待c1 <- i
完成,因为还有一个额外的东西c1
.我发现在实际代码中发生调试此类事情的最好方法是查看哪些goroutine被阻止并认真考虑.
如果确实需要,您也可以仅使用同步通道来回避问题.
归档时间: |
|
查看次数: |
9174 次 |
最近记录: |