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

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

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

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

Go
當年話下 2023-08-14 17:00:02
當我注意到并非管道中的所有關閉打印都被打印時,我根據《Go 中的并發》一書中的示例運行以下代碼??吹?amp;ldquo;完成倍增!&rdquo;?不見了。另一方面,NumGoroutine() 顯示僅主函數正在運行。下面的代碼有什么問題?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貢獻1873條經驗 獲得超9個贊

有些代碼路徑不會打印某些消息done。調度程序碰巧選擇了一個不打印 的那個multiply。如果您稍微更改代碼(例如,在與現在不同的實例上登錄),您會發現它也可能會錯過該消息add?done。原因如下:

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

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

處理此問題的更好方法是不使用 for 循環從通道讀取。例如:

for {

? ? ?select {

? ? ? ? case <-done:

? ? ? ? ? ?return

? ? ? ? case i, ok:= <-intstream:

? ? ? ? ? ?if !ok {

? ? ? ? ? ? ? return

? ? ? ? ? ?}

? ? ? ? ? ?select {

? ? ? ? ? ? ? ?case <- done:

? ? ? ? ? ? ? ? ? ? return

? ? ? ? ? ? ? ?case addedStream <- i + additive:

? ? ? ? ? ?}

? ? ?}

}


查看完整回答
反對 回復 2023-08-14
?
一只甜甜圈

TA貢獻1836條經驗 獲得超5個贊

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

如果我添加fmt.Println調用來公開它們由于到達輸入流末尾而退出的點,則行為會略有變化(可能是由于時間原因;同時我正在輸入這個),輸出變成:

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)。


查看完整回答
反對 回復 2023-08-14
  • 2 回答
  • 0 關注
  • 170 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

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

幫助反饋 APP下載

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

公眾號

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