go版本go1.11.4 darwin / amd64
创建了一个新频道和goroutine,并且旧频道的内容已通过goroutine转移到了新频道。它不应该阻止,但是经过测试,发现它被阻止了。
谢谢。
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
将被阻止。
答案 0 :(得分:2)
recv1Block()
的,它都会创建一个新通道并启动一个后台goroutine,尝试从中读取所有数据。由于您是在循环中调用它,因此您将有很多事情试图消耗通道中的数据。由于通道永远不会关闭,因此所有goroutine都将永远运行。
作为练习,您可以尝试将代码更改为绕过chan int
而不是chan struct{}
,并写出一系列序号,并在最终收到时打印出来。这样的序列是有效的:
run
在例行程序1上调用recv1Block()
。recv1Block()
阻止了频道2上的接收。run
从recv1Block()
中读取0
。w.c1
将recv1Block()
写入通道#2(其中GR#1上的0
可以读取)。run
从recv1Block()
中读取1
。w.c1
想将recv1Block()
写入通道2,但会阻塞。0
。run
在GR#1上调用0
。run
从recv1Block()
中读取recv1Block()
。请注意,此序列中的值1永远不会写入任何地方,实际上,没有任何东西可以读取它。
这里的简单解决方案是不循环调用频道创建函数:
recv1Block()
完成频道后,关闭频道也是一种标准做法。这将终止2
循环,并且对w.c1
语句可读。在您的顶级生成器函数中:
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
这也意味着您不需要通过“航位推算”来运行主循环,因为它可以精确地产生100个项目然后停止;您可以在频道退出时停止播放。我上面显示的消费者循环可以做到这一点。