kal*_*ant 2 concurrency channel go
我正在编写一些代码,将数据从一个通道传递到另一个通道。根据一些直觉和这个答案,我希望以下代码可以工作(other是一个足够大的缓冲通道,并且out是源通道):
for {
select {
case other <- (<-out):
log.Warn("C")
}
}
Run Code Online (Sandbox Code Playgroud)
确实如此!但其他情况根本不会触发,例如D下面代码的日志中没有s:
for {
select {
case other <- (<-out):
log.Warn("C")
default:
log.Warn("D")
}
}
Run Code Online (Sandbox Code Playgroud)
使用更传统的解决方案,D日志中到处都是:
for {
select {
case msg := <-out:
other <- msg
log.Warn("C")
default:
log.Warn("D")
}
}
Run Code Online (Sandbox Code Playgroud)
显然,我将采用通常的解决方案,但我仍然不知道为什么不寻常的解决方案不能按预期工作。
我怀疑答案就在Go Memory Model 的某个地方,但我无法弄清楚在这种情况下到底发生了什么。
我整理了一些游乐场,您可以在其中查看这种行为:
在此先感谢任何可以对此有所了解的人!
当你有这个时:
ch := make(chan int, 10)
// ...
select {
case ch <- <-out:
fmt.Println("C")
default:
fmt.Println("D")
}
Run Code Online (Sandbox Code Playgroud)
第一个的通信操作case是ch <- something,哪里something是<-out。但是something首先评估 ,然后才检查案例的哪个通信操作可以继续。
所以<-out会根据需要阻塞,然后ch <- something检查是否可以继续。由于您使用了足够大的缓冲区,因此它始终可以在您的示例中继续进行,因此default永远不会被选中。
“select”语句的执行分几个步骤:
- 对于语句中的所有情况,接收操作的通道操作数以及发送语句的通道和右侧表达式在进入“选择”语句时按源顺序被计算一次。结果是一组要接收或发送的通道,以及要发送的相应值。无论选择哪个(如果有)通信操作进行,该评估中的任何副作用都会发生。带有简短变量声明或赋值的 RecvStmt 左侧的表达式尚未计算。
- 如果可以进行一个或多个通信,则通过统一伪随机选择选择可以进行的单个通信。否则,如果存在默认情况,则选择该情况。如果没有默认情况,“select”语句会阻塞,直到至少有一个通信可以继续。
- 除非选择的情况是默认情况,否则将执行相应的通信操作。
- 如果选定的 case 是带有短变量声明或赋值的 RecvStmt,则评估左侧表达式并分配接收到的值(或多个值)。
- 执行所选案例的语句列表。
如果您降低 的缓冲区ch,您会看到D输出中偶尔会打印 s(在Go Playground上尝试)。
ch := make(chan int, 2)
Run Code Online (Sandbox Code Playgroud)