簡體   English   中英

退出等待無緩沖通道的多個 go 例程

[英]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()進行取消。

有問題的代碼不能保證工作。 這是一個失敗的場景:

  1. 一個 goroutine 已准備好從exit接收。 所有其他 goroutines 在其他地方忙。
  2. main 發送的值由 ready goroutine 接收。
  3. 該 goroutine 向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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM