[英]exiting multiple go routines waiting on an unbuffered channel
我试图同时退出多个 Goroutines。 根据https://www.godesignpatterns.com/2014/04/exiting-multiple-goroutines-simultaneously.html有一个定义明确的方法。
我看到的另一种方法如下
package main
import (
"fmt"
"time"
)
func main() {
var inCh chan int = make(chan int, 100)
var exit chan bool = make(chan bool)
for i := 0; i < 20; i++ {
go func(instance int) {
fmt.Println("In go routine ", instance)
for {
select {
case <-exit:
fmt.Println("Exit received from ", instance)
exit <- true
return
case value := <-inCh:
fmt.Println("Value=", value)
}
}
}(i)
}
time.Sleep(1 * time.Second)
exit <- true
<-exit // Final exit
fmt.Println("Final exit")
}
但我很困惑,我真的不明白为什么最后的无缓冲通道作为最后一条语句执行。 实际上我有 20 个 go 例程监听退出通道。 一个人会随机收到它并将其发送给另一个人。 为什么go例程中的接收总是在发生,只有当所有例程都完成时,才会执行注释为“// Final Exit”的通道接收?
如果有人能给我一个解释,我将不胜感激。
如链接文章中所示,使用close()
进行取消。
有问题的代码不能保证工作。 这是一个失败的场景:
exit
接收。 所有其他 goroutines 在其他地方忙。exit
发送一个值,该值由main()
接收。 其他 goroutine 不会退出,因为没有更多的值被发送到exit
。 请参阅这个使用 time.Seep 来引发问题场景的游乐场示例。
为什么go例程中的接收总是在发生,只有当所有例程都完成时,才会执行注释为“// Final Exit”的通道接收?
该程序的执行就像通道维护一个有序的等待 goroutines 队列一样,但规范中没有任何内容可以保证该行为。 即使通道有一个有序队列,如果 goroutine 正在做一些事情而不是等待从exit
接收,程序也会遇到上面的场景。
如果你注意到你程序的 output
In go routine 6
In go routine 0
In go routine 7
.
.
Exit received from 6
Exit received from 0
Exit received from 7
.
.
Final exit
他们以与他们开始时相同(或几乎相同)的顺序被调用。 如果您的 Go 例程都不忙,将使用第一个注册的例程。 这只是运行时的一个实现,我不会指望这种行为。
您的最终退出是要收听的最后一个频道,因此最后使用。
如果你删除 time.Sleep 在你的循环之后你的最终退出将几乎立即被调用并且你的大部分 go 例程将不会收到退出信号
Output 没有时间。睡眠(将在运行之间)
In go routine 0
Exit received from 0
In go routine 1
In go routine 2
In go routine 3
In go routine 4
In go routine 5
In go routine 6
In go routine 7
In go routine 14
In go routine 15
In go routine 16
In go routine 17
In go routine 18
In go routine 19
Final exit
考虑这个轻微的修改。
package main
import (
"fmt"
)
func main() {
var exit chan int = make(chan int)
var workers = 20
for i := 0; i < workers; i++ {
go func(instance int) {
fmt.Println("In go routine ", instance)
for {
select {
case i := <-exit:
fmt.Println("Exit", i, "received from ", instance)
exit <- i-1
return
}
}
}(i)
}
exit <- workers
fmt.Println("Final exit:", <-exit)
}
在这里,我做了三件事:首先,为简洁起见,我删除了未使用的频道。 其次,我删除了睡眠。 第三,我将exit
通道更改为每次通过都会递减的int
通道。 如果我传递工人的数量,“最终”消息中除0
以外的任何值都表示工人被丢弃。
这是一个示例运行:
% go run t.go
In go routine 8
In go routine 5
In go routine 0
In go routine 2
Exit 20 received from 8
Exit 19 received from 5
Final exit: 18
In go routine 13
当main
调用time.Sleep
时,直到睡眠结束才会安排它。 其他 goroutines 都有这个时间来设置他们的频道阅读器。 我只能假设,因为我在任何地方都找不到它,通道读者可能会大致按时间顺序排队 - 因此, sleep
保证main
的读者是最后一个。
如果这是一贯的行为,那肯定是不可靠的。
请参阅多个 goroutines listening on one channel了解更多关于此的想法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.