3 回答
TA贡献1848条经验 获得超6个赞
不保证此代码可以正常工作。你可能会很幸运,但显然你一直很不走运。您可能会很幸运,并且可能会发生以下情况:
假设我们有两个 goroutine,A 和 B,其中 A 是运行的 goroutine main
,B 是运行匿名函数的 goroutine。可能会发生以下情况:
B:执行
select
;没有人在quit
频道上写,所以执行default
此案B:执行
<-buffer
,所以开始阻塞,等待有人写信buffer
答:写“走!” 到
buffer
B:收到“Go!” 并打印出来。继续循环。
A:写真实的
quit
B:执行
select
;A 正在尝试写入quit
,因此请执行该案例。打印“再见!” 并返回A: 既然写完了,继续从
main
但是,这不保证会发生。特别是,在读取 之后buffer
,B 可能会继续执行,执行,并在 A 有机会写入之前select
陷入这种情况。这就是可能发生的事情,看起来像这样:default
quit
B:执行
select
;没有人在quit
频道上写,所以执行default
此案B:执行
<-buffer
,所以开始阻塞,等待有人写信buffer
答:写“走!” 到
buffer
B:收到“Go!” 并打印出来。继续循环
B:执行
select
;没有人在quit
频道上写,所以执行default
此案B:执行
<-buffer
,所以开始阻塞,等待有人写信buffer
A: 写 true to
quit
,所以开始阻塞,等待有人读取quit
现在 A 和 B 都被阻塞了,并且由于系统中没有其他 goroutines,因此没有任何事件可以解除阻塞,因此系统卡住了。
解决方案
解决此问题的一种方法是使 goroutine B 从buffer
作为其中一种select
情况而不是在选择情况中读取。这样,select
将简单地阻塞,直到任一通道可用于操作,并且您的代码将按照您可能希望的方式运行:
select {
case <-quit:
fmt.Println("Bye!")
return
case str := <-buffer:
fmt.Println(str)
}
在 Go Playground 上看到它。
但是请注意,由于maingoroutine 在写入quit通道后立即返回,并且整个 Go 程序在这种情况发生时立即退出,因此您可能(并且可能会)不走运并且fmt.Println("Bye!")不会在程序退出之前执行。
TA贡献1796条经验 获得超10个赞
将选择默认子句更改为
default:
fmt.Println("waiting on <-buffer")
fmt.Println(<-buffer)
看看发生了什么。
问题是 goroutine 在执行之前main执行select 中的默认分支quit <- true。
goroutine 阻塞 at fmt.Println(<-buffer),main函数阻塞 at quit <- true。
为防止死锁,请在 case 语句中接收:
select {
case <-quit:
fmt.Println("Bye!")
return
case msg := <-buffer:
fmt.Println(msg)
}
TA贡献1796条经验 获得超4个赞
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?
}
- 3 回答
- 0 关注
- 157 浏览
添加回答
举报