https://play.golang.org/p/FyIUPkwq0R
为什么以下死锁?
package main
import (
"fmt"
)
var quit chan bool
var buffer chan string
func main() {
buffer = make(chan string)
quit = make(chan bool)
go func() {
i:=0
for {
select {
case <- quit:
fmt.Println("Bye!")
return
default:
fmt.Println(<-buffer)
}
i++
fmt.Println(i)
}
}()
buffer <- "Go!"
quit <- true // This line dead locks
//buffer <- "Hello" // When I do this instead it works?
//quit <- true // Also when I don't quit it still exit's?
}
答案 0 :(得分:3)
此代码无法正常运行。你可能会很幸运,但显然你已经不幸了。你可能会很幸运,可能会发生以下情况:
让我们说我们有两个goroutines,A和B,其中A是运行main
的goroutine,B是运行匿名函数的goroutine。可能会发生以下情况:
select
;没有人在quit
频道上撰稿,因此请执行default
案例<-buffer
,所以开始阻止,等待有人写信给buffer
buffer
quit
select
; A正在尝试写入quit
,因此执行该案例。打印&#34;再见!&#34;并返回main
然而,这并不能保证会发生。特别是,在从buffer
读取之后,B可能会继续执行,执行select
,并在A有机会写入default
之前落入quit
案例。那可能发生了什么,看起来像这样:
select
;没有人在quit
频道上撰稿,因此请执行default
案例<-buffer
,所以开始阻止,等待有人写信给buffer
buffer
select
;没有人在quit
频道上撰稿,因此请执行default
案例<-buffer
,所以开始阻止,等待有人写信给buffer
quit
写真,所以开始阻止,等待某人阅读quit
现在A和B都被阻止,并且由于系统中没有其他goroutine,因此任何事件都无法解除阻塞,因此系统卡住了。
解决这个问题的一种方法是使goroutine B从buffer
读取select
个案例而不是选择案例。这样,select
将一直阻塞,直到任一通道可用于某个操作,并且您的代码将按照您可能想要的那样运行:
select {
case <-quit:
fmt.Println("Bye!")
return
case str := <-buffer:
fmt.Println(str)
}
在Go Playground上查看here。
但请注意,由于main
goroutine在写入quit
频道后立即返回,并且整个Go程序在发生这种情况时立即退出,您可能(也可能会) )不幸的是,fmt.Println("Bye!")
在程序退出之前不会执行。
答案 1 :(得分:1)
将select default子句更改为
default:
fmt.Println("waiting on <-buffer")
fmt.Println(<-buffer)
看看发生了什么。
问题是goroutine在main
执行quit <- true
之前执行选择中的默认分支。
goroutine阻挡了fmt.Println(<-buffer)
和main
功能块quit <- true
。
为了防止死锁,请在case语句中接收:
select {
case <-quit:
fmt.Println("Bye!")
return
case msg := <-buffer:
fmt.Println(msg)
}
答案 2 :(得分:0)
package main
import (
"fmt"
)
var quit chan bool
var buffer chan string
func main() {
buffer = make(chan string)
quit = make(chan bool)
go func() {
i := 0
for {
select {
case <-quit:
fmt.Println("Bye!")
return
case v := <-buffer:
fmt.Println(v)
default:
// 这里也可能 block
// fmt.Println(<-buffer)
}
i++
fmt.Println(i)
}
}()
buffer <- "Go!"
quit <- true // This line dead locks
//buffer <- "Hello" // When I do this instead it works?
//quit <- true // Also when I don't quit it still exit's?
}