概述
golang channel 是我们常用的一个用于处理协程通信的最重要的工具, 在一些边界情况下的表现,差异化很大。
本文就golang在channel正常状态、关闭状态、nil 状态下的发送接收以及关闭的表现做一个简单的测试说明, 有任何疑问欢迎交流探讨。
channel在各种状态下的操作表现结论
| 通道操作 | 正常状态 | 关闭状态 | nil |
|---|---|---|---|
| 发送 | √ | X (panic: send on closed channel) | X |
| 接收 | √ | √ (接收到的为初始值) | X |
| 关闭 | √ | X (panic: close of closed channel) | X |
正常发送和接收
示例代码
package main
import (
"fmt"
)
func main() {
ch := make(chan string)
go send(ch)
recv(ch)
close(ch) // 关闭
}
// 接收
func recv(ch <-chan string) {
x := <-ch
fmt.Println("result:", x)
}
// 发送
func send(ch chan<- string) {
ch <- "send_val"
}
运行结果: (一切表现正常)
result: send_val
关闭状态的channel进行操作
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan string)
close(ch) // ok
close(ch) // panic: close of closed channel
go send(ch) // panic: send on closed channel
recv(ch) // result:
time.Sleep(time.Second)
}
func send(ch chan<- string) {
ch <- "send_val"
}
func recv(ch <-chan string) {
x := <-ch
fmt.Println("result:", x)
}
nil channel的操作表现
- 发送的时候: 情况稍复杂, 不存在接收者的时候, 则阻塞, 存在接受者的时候则直接报错
fatal error: all goroutines are asleep - deadlock! - 关闭的时候: 异常
panic: close of nil channel - 接收的时候: 有接收者存在的时候错误
fatal error: all goroutines are asleep - deadlock!, 无接收者存在的时候则block。
这些都是表象,究其根本原因还在于golang channel的底层实现,channel在发送或者接收的时候并不会直接检查channel 是否可用,而是先看看存在不存在对应会调度接受或者发送的go routine, 如果不存在,则挂起阻塞, 存在之后,才会通过这个 nil 的channel进行发送或者接收, 发送/接收的时候会发现channel非法报 goroutine 1 [chan receive (nil chan)]: 然后另外一端则继续进入阻塞状态
package main
import (
"fmt"
"time"
)
func main() {
var ch chan string
close(ch) // panic: close of nil channel
go recv(ch) // goroutine 1 [chan receive (nil chan)]:
send(ch) // fatal error: all goroutines are asleep - deadlock!
time.Sleep(time.Second)
}
func send(ch chan<- string) {
ch <- "send_val"
}
func recv(ch <-chan string) {
x := <-ch
fmt.Println("result:", x)
}