[英]exiting multiple go routines waiting on an unbuffered channel
I was trying to exit multiple Goroutines Simultaneously.我试图同时退出多个 Goroutines。 According to https://www.godesignpatterns.com/2014/04/exiting-multiple-goroutines-simultaneously.html there is a well defined way to do it.
根据https://www.godesignpatterns.com/2014/04/exiting-multiple-goroutines-simultaneously.html有一个定义明确的方法。
Another approach i see is the following我看到的另一种方法如下
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")
}
But I am confused and i really don't get it why the unbuffered channel at the end is executed as a last statement.但我很困惑,我真的不明白为什么最后的无缓冲通道作为最后一条语句执行。 In reality I have 20 go routines listening for exit channel.
实际上我有 20 个 go 例程监听退出通道。 Randomly one will receive it and send it to another.
一个人会随机收到它并将其发送给另一个人。 Why always the reception within go routines is taking place and only when all of them are finished the channel reception with comment "// Final Exit" will execute?
为什么go例程中的接收总是在发生,只有当所有例程都完成时,才会执行注释为“// Final Exit”的通道接收?
I will really appreciate if someone can give me an explanation.如果有人能给我一个解释,我将不胜感激。
Use close()
for cancelation as shown in the linked article.如链接文章中所示,使用
close()
进行取消。
The code in question is not guaranteed to work.有问题的代码不能保证工作。 Here's a scenario where it fails:
这是一个失败的场景:
exit
.exit
接收。 All other goroutines busy somewhere else.exit
that is received by main()
.exit
发送一个值,该值由main()
接收。 The other goroutines do not exit because no more values are sent to exit
.其他 goroutine 不会退出,因为没有更多的值被发送到
exit
。 See this playground example that uses time.Seep to induce the problem scenario.请参阅这个使用 time.Seep 来引发问题场景的游乐场示例。
Why always the reception within go routines is taking place and only when all of them are finished the channel reception with comment "// Final Exit" will execute?
为什么go例程中的接收总是在发生,只有当所有例程都完成时,才会执行注释为“// Final Exit”的通道接收?
The program executes as if the channel maintains an ordered queue of waiting goroutines, but there's nothing in the specification that guarantees that behavior.该程序的执行就像通道维护一个有序的等待 goroutines 队列一样,但规范中没有任何内容可以保证该行为。 Even if the channel has an ordered queue, the program can encounter the scenario above if a goroutine is doing something other than waiting on receive from
exit
.即使通道有一个有序队列,如果 goroutine 正在做一些事情而不是等待从
exit
接收,程序也会遇到上面的场景。
If you notice the output of you program如果你注意到你程序的 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
They get called in the same( or almost the same) order of how they were started.他们以与他们开始时相同(或几乎相同)的顺序被调用。 If none of your Go routines are busy the first one registered will be used.
如果您的 Go 例程都不忙,将使用第一个注册的例程。 This is just an implementation of the runtime and I would not count on this behavior.
这只是运行时的一个实现,我不会指望这种行为。
Your final exit was the last channel to be listened on so it is used last.您的最终退出是要收听的最后一个频道,因此最后使用。
If you remove the time.Sleep after your loop your final exit will get called almost immediately and most of your go routines will not receive the exit signal如果你删除 time.Sleep 在你的循环之后你的最终退出将几乎立即被调用并且你的大部分 go 例程将不会收到退出信号
Output with out time.Sleep (will very between runs) 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
Consider this slight modification.考虑这个轻微的修改。
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)
}
Here, I've done 3 things: First, I removed the unused channel, for brevity.在这里,我做了三件事:首先,为简洁起见,我删除了未使用的频道。 Second, I removed the sleep.
其次,我删除了睡眠。 third, I changed the
exit
channel to an int
channel that is decremented by every pass.第三,我将
exit
通道更改为每次通过都会递减的int
通道。 If I pass the number of workers in, any value other than 0
from the "Final" message indicates dropped workers.如果我传递工人的数量,“最终”消息中除
0
以外的任何值都表示工人被丢弃。
Here's one example run:这是一个示例运行:
% 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
When main
calls time.Sleep
it doesn't get scheduled until the sleep is over.当
main
调用time.Sleep
时,直到睡眠结束才会安排它。 The other goroutines all have this time to set up their channel readers.其他 goroutines 都有这个时间来设置他们的频道阅读器。 I can only assume, because I can't find it written anywhere, that channel readers are likely to be queued in roughly chronological error - thus, the
sleep
guarantees that main
's reader is the last.我只能假设,因为我在任何地方都找不到它,通道读者可能会大致按时间顺序排队 - 因此,
sleep
保证main
的读者是最后一个。
If this is consistent behavior, it certainly isn't reliable .如果这是一贯的行为,那肯定是不可靠的。
See Multiple goroutines listening on one channel for many more thoughts on this.请参阅多个 goroutines listening on one channel了解更多关于此的想法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.