ch11type/chan: selectnbsend 函数非阻塞操作
cnbailian opened this issue · comments
Lian Bai commented
动机
我在文章的上半部分发现了 Go 对于 select 非阻塞 channel 操作有这样一个特性:可以使用 select 可以向 nil channel 发送消息而不会死锁
需求说明
相关源码:
func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
if c == nil {
if !block {
return false
}
gopark(nil, nil, waitReasonChanSendNilChan, traceEvGoStop, 2)
throw("unreachable")
}
(......)
}
我暂时也想不出来哪种场景会出现 select 向 nil channel 发送消息的场景,但是 Go 设计了这个特性,我觉得可以在文章中讲 block
值为 false
时提一下。
其他
在 select 只有一个 case 时,会检测出这种情况,并且当作 no cases
直接死掉了
func main() {
var ch chan int
select {
case ch <- 1:
(......)
}
}
// fatal error: all goroutines are asleep - deadlock!
// goroutine 1 [select (no cases)]:
如果有 default,或其他正常 case,就可以使用这个特性,不太清楚 select 在哪里做了处理。
Changkun Ou commented
你说的这种情况确实存在,而且仅在只有两个 case 且其中一个 case 是 default 时才能使用。
这种情况是一种编译器的优化,并在在 walkselectcases
这部分进一部分简单提及的。不过这段内容比较靠后:
似乎与源码中发生的行为并不一致,因为按照调用,当锁被解除后,并没有任何 panic。
这是为什么呢?事实上,通过对程序进行反编译,我们能够观察到,**当 select 语句只有一个 case 时,`select` 关键字
是没有被翻译成 `selectgo` 的。因为只有一个 case 的 `select` 与 `if` 是没有区别的,这也是编译器本身对代码的一个优化,
消除了这种情况下调用 `selectgo` 的性能开销:
我考虑优化一下这部分的内容描述。
Lian Bai commented
明白了,虽然没看懂代码,但注释看懂了...
// if ch == nil { block() }; n;
当只有一个 case 并且 channel 值为 nil 的情况下,直接编译为 block
函数调用,也就是处理掉这种情况,直接作为 no case 处理。
thanks