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

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

Channel 有一個奇怪的行為,為什么要阻止?

Channel 有一個奇怪的行為,為什么要阻止?

Go
HUX布斯 2023-06-01 15:10:23
go 版本 go1.11.4 darwin/amd64創建了一個新的channel和goroutine,通過goroutine將舊channel的內容傳輸到new channel。應該不會阻塞,但是經過測試,發現是阻塞了。謝謝。type waiter struct {    ch1   chan struct{}    ch2   <-chan time.Time    limit int    count int}func (w *waiter) recv1Block() chan struct{} {    ch := make(chan struct{})    go func() {        for m := range w.ch1 {            ch <- m        }    }()    return ch}func (w *waiter) runBlock(wg *sync.WaitGroup) {    defer wg.Done()    i := 0    for i < w.limit {        select {        case <-w.recv1Block():  **// why block here?**            i++        case <-w.recv2():        }    }    w.count = i}為什么recv1Block會被block。
查看完整描述

1 回答

?
蠱毒傳說

TA貢獻1895條經驗 獲得超3個贊

每次調用時recv1Block(),它都會創建一個新通道并啟動一個后臺 goroutine,該 goroutine 會嘗試從中讀取所有數據。由于您是在循環中調用它,因此您將有很多東西都在嘗試使用通道中的數據;由于通道永遠不會關閉,所有的 goroutines 將永遠運行。

作為練習,您可以嘗試更改代碼以傳遞 achan int而不是 a chan struct{},并編寫一系列序列號,并在最終收到時將它們打印出來。像這樣的序列是有效的:

  1. run在 goroutine #1 上調用recv1Block()。

  2. recv1Block()在 GR#1 上生成 GR#2,并返回通道#2。

  3. run在 GR#1 上阻止在通道#2 上接收。

  4. recv1Block()在 GR#2 上讀取0w.c1.

  5. recv1Block()在 GR#2 上寫入0通道#2(run在 GR#1 上準備好讀取)。

  6. recv1Block()在 GR#2 上讀取1w.c1.

  7. recv1Block()GR#2 上想要寫入0通道#2 但阻塞了。

  8. runGR#1 上的喚醒,并接收到0。

  9. run在 GR#1 電話上recv1Block()。

  10. recv1Block()在 GR#1 上生成 GR#3,并返回通道 #3。

  11. recv1Block()在 GR#3 上讀取2w.c1.

  12. ...

請注意,此序列中的值 1 永遠不會被寫入任何地方,實際上沒有任何東西可以讀取它。

這里的簡單解決方案是不要在循環中調用通道創建函數:

func (w *waiter) runBlock(wg *sync.WaitGroup) {

    defer wg.Done()

    ch1 := w.recv1Block()

    ch2 := w.recv2()

    for {

        select {

        case _, ok := <-ch1:

            if !ok {

                return

            }

            w.count++

        case <-ch2:

    }

}

完成頻道后關閉頻道也是標準做法。這將終止一個for ... range ch循環,并且它看起來對select語句是可讀的。在您的頂級生成器函數中:


for i := 0; i < w.limit; i++ {

    w.ch1 <- struct{}{}

}

close(w.ch1)

在您的“復制頻道”功能中:


func (w *waiter) recv1Block() chan struct{} {

    ch := make(chan struct{})

    go func() {

        for m := range w.ch1 {

            ch <- m

        }

        close(ch)

    }()

    return ch

}

這也意味著您不需要通過“航位推算”運行主循環,期望它正好產生 100 個項目然后停止;只要它的頻道退出,你就可以停止。我上面顯示的消費者循環就是這樣做的。


查看完整回答
反對 回復 2023-06-01
  • 1 回答
  • 0 關注
  • 156 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

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

幫助反饋 APP下載

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

公眾號

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