dongyong1400 2019-01-15 01:27
浏览 17
已采纳

频道有一个奇怪的行为,为什么要阻止?

go version go1.11.4 darwin/amd64

A new channel and goroutine were created, and the content of the old channel was transferred to the new channel through goroutine. It should not block, but after testing, it was found to be blocked.

thanks.

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
}

why recv1Block will be block.

  • 写回答

1条回答 默认 最新

  • dongtou2016 2019-01-15 02:47
    关注

    Every time you call recv1Block(), it creates a new channel and launches a background goroutine that tries to read all of the data from it. Since you're calling it in a loop, you will have many things all trying to consume the data from the channel; since the channel never closes, all of the goroutines will run forever.

    As an exercise, you might try changing your code to pass around a chan int instead of a chan struct{}, and write a series of sequential numbers, and print them out as they're ultimately received. A sequence like this is valid:

    1. run on goroutine #1 calls recv1Block().
    2. recv1Block() on GR#1 spawns GR#2, and returns channel#2.
    3. run on GR#1 blocks receiving on channel#2.
    4. recv1Block() on GR#2 reads 0 from w.c1.
    5. recv1Block() on GR#2 writes 0 to channel#2 (where run on GR#1 is ready to read).
    6. recv1Block() on GR#2 reads 1 from w.c1.
    7. recv1Block() on GR#2 wants to write 0 to channel#2 but blocks.
    8. run on GR#1 wakes up, and receives the 0.
    9. run on GR#1 calls recv1Block().
    10. recv1Block() on GR#1 spawns GR#3, and returns channel #3.
    11. recv1Block() on GR#3 reads 2 from w.c1.
    12. ...

    Notice that the value 1 in this sequence will never be written anywhere, and in fact there is nothing left that could read it.

    The easy solution here is to not call the channel-creating function in a loop:

    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:
        }
    }
    

    It's also standard practice to close channels when you're done with them. This will terminate a for ... range ch loop, and it will appear as readable to a select statement. In your top-level generator function:

    for i := 0; i < w.limit; i++ {
        w.ch1 <- struct{}{}
    }
    close(w.ch1)
    

    And in your "copy the channel" function:

    func (w *waiter) recv1Block() chan struct{} {
        ch := make(chan struct{})
        go func() {
            for m := range w.ch1 {
                ch <- m
            }
            close(ch)
        }()
        return ch
    }
    

    This also means that you don't need to run the main loop by "dead reckoning", expecting it to produce exactly 100 items then stop; you can stop whenever its channel exits. The consumer loop I show above does this.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 拟通过pc下指令到安卓系统,如果追求响应速度,尽可能无延迟,是不是用安卓模拟器会优于实体的安卓手机?如果是,可以快多少毫秒?
  • ¥20 神经网络Sequential name=sequential, built=False
  • ¥16 Qphython 用xlrd读取excel报错
  • ¥15 单片机学习顺序问题!!
  • ¥15 ikuai客户端多拨vpn,重启总是有个别重拨不上
  • ¥20 关于#anlogic#sdram#的问题,如何解决?(关键词-performance)
  • ¥15 相敏解调 matlab
  • ¥15 求lingo代码和思路
  • ¥15 公交车和无人机协同运输
  • ¥15 stm32代码移植没反应