我试图在默认情况下理解选择块中的闭合通道的行为,但是对此后续输出感到困惑。 这里正在调用50个goroutines并关闭终点频道。
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打印&#34;收到完成&#34;,默认情况不应该由任何其他goroutines执行,即&#34;我没有等待&#34;不应该打印。
但输出并不一致。有时它表现得如预期,但在多次运行时,我可以看到意外的输出如下:
=====output======
I didnt wait 0
received finish 7
finish closed
received finish 13
received finish 10
received finish 32
received finish 5
received finish 14
received finish 33
received finish 42
received finish 11
received finish 4
received finish 23
received finish 44
received finish 49
received finish 15
received finish 24
received finish 31
received finish 16
received finish 40
received finish 41
received finish 6
received finish 26
I didnt wait 1
received finish 19
received finish 8
received finish 43
received finish 29
received finish 20
received finish 46
received finish 12
received finish 36
received finish 47
received finish 37
received finish 35
received finish 30
received finish 39
received finish 22
received finish 28
I didnt wait 2
received finish 17
received finish 45
I didnt wait 9
received finish 48
received finish 34
I didnt wait 3
received finish 25
received finish 38
received finish 27
received finish 18
received finish 21
Waited 394.999µs for 50 goroutines to stop
我正在经历this link期待接近(完成)会发出其他人,即那些仍在等待的人,表现出相似的行为。
答案 0 :(得分:2)
对fmt.Printf的调用涉及系统调用。 Syscalls会自动导致重新安排goroutine,因为它必须等待操作系统完成该系统调用。这意味着其中一些goroutine很可能运行select语句并选择默认情况,但不打印到控制台。
编辑:此外,如果您在具有多个线程的系统上运行此操作,默认情况下,go运行时将并行运行多个go例程 (匹配OS线程的数量),这意味着其中一些goroutine可以在通道关闭的同时执行,并在主goroutine中关闭通道之前到达select语句。
如果您添加同步通道以确保在任何goroutine中发生select
之前发生通道关闭操作,它将按预期工作:
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)
}