我试图了解默认情况下select块中封闭 channel 的行为,但与以下输出混淆。
这里调用了50个goroutine,并关闭了完成 channel 。
func testClosedChannelBehavior() {
const n = 50
finish := make(chan bool)
var done sync.WaitGroup
for i := 0; i < n; i++ {
done.Add(1)
go func(x int) {
select {
case <-time.After(1 * time.Hour):
case <-finish:
fmt.Printf("received finish %d\n", x)
default:
fmt.Printf("I didnt wait %d\n", x)
}
done.Done()
}(i)
}
t0 := time.Now()
close(finish)
fmt.Println("finish closed")
done.Wait()
fmt.Printf("Waited %v for %d goroutines to stop\n", time.Since(t0), n)
}
我希望一旦任何goroutine打印“接收完成”,默认情况下就不会被任何其他goroutine执行,即“我没有等待”不应该被打印。
但是输出不一致。有时它的行为符合预期,但是在多次运行时,我可能会看到以下意外输出:
=====output======I didnt wait 0received finish 7finish closedreceived finish 13received finish 10received finish 32received finish 5received finish 14received finish 33received finish 42received finish 11received finish 4received finish 23received finish 44received finish 49received finish 15received finish 24received finish 31received finish 16received finish 40received finish 41received finish 6received finish 26I didnt wait 1received finish 19received finish 8received finish 43received finish 29received finish 20received finish 46received finish 12received finish 36received finish 47received finish 37received finish 35received finish 30received finish 39received finish 22received finish 28I didnt wait 2received finish 17received finish 45I didnt wait 9received finish 48received finish 34I didnt wait 3received finish 25received finish 38received finish 27received finish 18received finish 21Waited 394.999µs for 50 goroutines to stop
我正在经历this link,期望close(finish)会发出信号,告知仍在等待的其他人也表现相似。
最佳答案
调用fmt.Printf涉及系统调用。系统调用会自动重新安排该goroutine的时间,因为它必须在操作系统上等待以完成该系统调用。这意味着其中某些goroutine非常有可能运行select语句并选择默认大小写,但尚未打印到控制台。
编辑:另外,如果您在具有多个线程的系统上运行此程序,则默认情况下,go运行时将并行运行多个go例程(与OS线程数匹配),这意味着其中一些goroutine可能正在执行在主goroutine中,在 channel 关闭的同时,并在 channel 关闭之前到达select语句。
如果您添加一个同步 channel 以确保在任何goroutine中select
发生之前都进行 channel 关闭操作,则它将按预期工作:
https://play.golang.org/p/XtUYaihKgRT
func testClosedChannelBehavior() {
const n = 50
finish := make(chan bool)
proceed := make(chan struct{})
var done sync.WaitGroup
for i := 0; i < n; i++ {
done.Add(1)
go func(x int) {
<-proceed
select {
case <-time.After(1 * time.Hour):
case <-finish:
fmt.Printf("received finish %d\n", x)
default:
fmt.Printf("I didnt wait %d\n", x)
}
done.Done()
}(i)
}
t0 := time.Now()
close(finish)
fmt.Println("finish closed")
close(proceed)
done.Wait()
fmt.Printf("Waited %v for %d goroutines to stop\n", time.Since(t0), n)
}
关于go - 在具有选择大小写和默认值的Goroutines中,一旦 channel 关闭,默认值就不会执行,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48527601/