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

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.

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.