亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

關閉完成通道后 goroutine 缺少打印

關閉完成通道后 goroutine 缺少打印

Go
慕工程0101907 2022-04-26 16:03:02
當我注意到并非管道中的所有關閉打印都被打印時,我正在根據“Go 中的并發”一書中的示例運行以下代碼??吹健巴瓿杀对?!” 不見了。另一方面,NumGoroutine() 只顯示 main 函數正在運行。以下代碼有什么問題?(https://play.golang.org/p/tkFgvKboVgS)package mainimport (    "fmt"    "runtime"    "time")func main() {    generator := func(done <-chan struct{}) <-chan int {        intStream := make(chan int)        i:=0        go func() {            defer close(intStream)            for {                select {                case <-done:                    fmt.Println("done generator!")                    return                case intStream <- i:                    time.Sleep(1 * time.Second)                    i++                }                fmt.Println("generator after select")            }        }()        return intStream    }    multiply := func(        done <-chan struct{},        intStream <-chan int,        multiplier int,    ) <-chan int {        multipliedStream := make(chan int)        go func() {            defer close(multipliedStream)            for i := range intStream {                select {                case <-done:                    fmt.Println("done multiply !")                    return                case multipliedStream <- i * multiplier:                }                fmt.Println("multiply after select")            }        }()        return multipliedStream    }    add := func(        done <-chan struct{},        intStream <-chan int,        additive int,    ) <-chan int {        addedStream := make(chan int)        go func() {            defer close(addedStream)            for i := range intStream {                select {                case <-done:                    fmt.Println("done add !")                    return                case addedStream <- i + additive:                }                fmt.Println("add after select")            }        }()        return addedStream    }
查看完整描述

2 回答

?
隔江千里

TA貢獻1906條經驗 獲得超10個贊

有些代碼路徑不會打印某些done消息。調度程序碰巧選擇了一個不為multiply. 如果您稍微更改代碼(例如,在與現在不同的實例上登錄),您會發現它也可能會丟失add done消息。(https://play.golang.org/p/meEPM5GR9Rr)。原因如下:


如果done消息在生成器將數字寫入通道并且乘法器讀取它之后立即到達,那么乘法器會看到done可用并選擇它。multiplier打印done消息時就是這種情況。如果done消息在 multiplier 在 for 循環中等待時到達,則 multiplier 將接收輸入通道(而不是done通道)的關閉,導致 for 循環終止而不打印done消息。


出現問題是因為您正在從 for 循環中的通道讀取,然后進行選擇。在等待 for 循環從通道中讀取數據時,不會評估與選擇相關的任何事件。


解決這個問題的更好方法是不使用 for 循環從通道中讀取。例如:


for {

     select {

        case <-done:

           return

        case i, ok:= <-intstream:

           if !ok {

              return

           }

           select {

               case <- done:

                    return

               case addedStream <- i + additive:

           }

     }

}


查看完整回答
反對 回復 2022-04-26
?
拉丁的傳說

TA貢獻1789條經驗 獲得超8個贊

你的add和multiply例程不是永遠的循環,而是for ... range循環。因此,在每個循環的頂部,它們等待下一個整數,而不是等待select接收關閉done或將結果發送到它們的流。這不是問題,但這意味著如果它們的輸入流關閉,它們將返回而不進入循環本身。


如果我添加fmt.Println調用以暴露由于到達輸入流的末尾而退出的點,則行為會略有變化(可能是由于時間的原因;我沒有費心去解釋它,而Burak Serdar已經發布了他的答案我正在輸入這個),輸出變為:


add after select

2

multiply after select

generator after select

multiply after select

add after select

4

generator after select

multiply after select

add after select

6

generator after select

Closed done

done multiply !

add got end of stream - done!

finished iterating pipeline

generator after select

done generator!

ramaining goroutines: 1

finished!

通常更合理的是只讓生成器本身接收done信號,并使流水線函數始終寫入所有結果,這使得它們更可預測。當然,讀取每個管道的人必須讀到最后——但你已經在主 goroutine 中這樣做了,所以我們只是在整個過程中傳播它。 這是以這種方式執行的代碼的簡化版本;它輸出:


2

generator after select

4

generator after select

6

generator after select

Closed done

8

generator after select

done generator!

multiply got end of stream - done!

add got end of stream - done!

finished iterating pipeline

remaining goroutines: 1

請注意,這一次,我們從最終生成的值 (3) 中得到最終的計算值 (8)。


查看完整回答
反對 回復 2022-04-26
  • 2 回答
  • 0 關注
  • 158 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號