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 这个复选框什么作用?
  • ¥15 单通道放大电路的工作原理
  • ¥30 YOLO检测微调结果p为1
  • ¥20 求快手直播间榜单匿名采集ID用户名简单能学会的
  • ¥15 DS18B20内部ADC模数转换器
  • ¥15 做个有关计算的小程序
  • ¥15 MPI读取tif文件无法正常给各进程分配路径
  • ¥15 如何用MATLAB实现以下三个公式(有相互嵌套)
  • ¥30 关于#算法#的问题:运用EViews第九版本进行一系列计量经济学的时间数列数据回归分析预测问题 求各位帮我解答一下
  • ¥15 setInterval 页面闪烁,怎么解决