2 回答
TA贡献1873条经验 获得超9个赞
有些代码路径不会打印某些消息done。调度程序碰巧选择了一个不打印 的那个multiply。如果您稍微更改代码(例如,在与现在不同的实例上登录),您会发现它也可能会错过该消息add done。原因如下:
如果done消息在生成器将数字写入通道并且乘法器读取它之后立即到达,则乘法器会看到该数字done可用并选择该数字。multiplier打印消息时就是这种情况done。如果当donemultiplier 在 for 循环中等待时消息到达,则 multiplier 将接收输入通道(而不是通道)上的关闭消息done,从而导致 for 循环终止而不打印done消息。
出现问题的原因是您正在 for 循环中读取通道,然后进行选择。在等待 for 循环从通道读取数据时,不会评估与 select 相关的任何事件。
处理此问题的更好方法是不使用 for 循环从通道读取。例如:
for {
select {
case <-done:
return
case i, ok:= <-intstream:
if !ok {
return
}
select {
case <- done:
return
case addedStream <- i + additive:
}
}
}
TA贡献1836条经验 获得超5个赞
你的add例程multiply不是永远循环,而是for ... range循环。因此,在每个循环的顶部,它们等待下一个整数,而不是等待从其流select接收关闭done或将结果发送到其流。这不是问题,但这意味着如果它们的输入流关闭,它们将返回而不进入循环本身。
如果我添加fmt.Println调用来公开它们由于到达输入流末尾而退出的点,则行为会略有变化(可能是由于时间原因;同时我正在输入这个),输出变成:
add after select
2
multiply after select
generator after select
multiply after select
add after select
4
generator after select
multiply after select
add after select
6
generator after select
Closed done
done multiply !
add got end of stream - done!
finished iterating pipeline
generator after select
done generator!
ramaining goroutines: 1
finished!
通常更合理的做法是仅让生成器本身接收done信号,并让管道函数始终写入其所有结果,这使它们更可预测。当然,无论谁正在读取每个管道,都必须读到最后——但是您已经在主 goroutine 中执行了此操作,因此我们只是将其传播到整个管道。 这是您的代码的简化版本,以这种方式执行此操作;它输出:
2
generator after select
4
generator after select
6
generator after select
Closed done
8
generator after select
done generator!
multiply got end of stream - done!
add got end of stream - done!
finished iterating pipeline
remaining goroutines: 1
请注意,这一次,我们从最终生成值 (3) 中得到最终计算值 (8)。
- 2 回答
- 0 关注
- 227 浏览
添加回答
举报
