简体   繁体   English

退出等待无缓冲通道的多个 go 例程

[英]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:这是一个失败的场景:

  1. One goroutine is ready to receive from exit .一个 goroutine 已准备好从exit接收。 All other goroutines busy somewhere else.所有其他 goroutines 在其他地方忙。
  2. The value sent by main is received by the ready goroutine. main 发送的值由 ready goroutine 接收。
  3. That goroutine sends a value to exit that is received by main() .该 goroutine 向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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM