channel必须先初始化才能使用;未初始化的channel为nil,读写会永久阻塞;make(chan int)创建无缓冲channel,make(chan string, 10)创建容量为10的缓冲channel。

channel必须先初始化才能使用
未初始化的 channel 是 nil,对它进行读或写操作会永久阻塞(goroutine 永久挂起),这是 Go 中最常见的死锁源头之一。
-
make(chan int)创建无缓冲 channel;make(chan string, 10)创建容量为 10 的缓冲 channel - 判断 channel 是否为 nil:直接用
ch == nil,但生产代码中应避免依赖此判断,而应确保初始化逻辑不被跳过 - 函数接收 channel 参数时,调用方传入 nil channel 是合法语法,但会导致调用即阻塞 —— 所以接口文档或注释里应明确要求“非 nil”
向已关闭的 channel 写数据会 panic
Go 运行时会在向已关闭的 chan 执行 ch 时触发 panic: send on closed channel。但读已关闭的 channel 是安全的:返回零值 + false(如果带 ok 返回)。
- 关闭 channel 的责任通常属于发送方;接收方不应 close,也不应假设自己能“最后一个关闭”
- 多个 goroutine 并发写同一 channel 时,不能靠“谁先 close 谁赢”,必须用额外同步机制(如
sync.Once)确保只 close 一次 - 常见误写:
close(ch) ch <- 42 // panic!
无缓冲 channel 的读写必须配对才不阻塞
无缓冲 channel 相当于一个同步点:每次 操作都要求对方 goroutine 同时准备好另一端操作,否则当前 goroutine 阻塞。
- 发送方阻塞直到有接收方就绪;接收方阻塞直到有发送方就绪
- 典型陷阱:在 main goroutine 中向无缓冲 channel 发送,但没启动接收 goroutine —— 程序立即死锁
- 调试建议:用
go run -gcflags="-l" main.go关闭内联后加log.Println("sent"),可快速定位卡在哪一行 - 示例(会死锁):
ch := make(chan int) ch <- 1 // main 卡在这里,永远等不到接收者
range 遍历 channel 会自动等待关闭
for v := range ch 本质是持续接收,直到 channel 关闭且缓冲区为空。它隐式处理了“读完所有已发送值再退出”的逻辑,比手动 for { select { case v := 更简洁。
- range 不会因 channel 暂时空而提前退出;它只响应
close()信号 - 如果发送方永不 close,range 永远不会结束 —— 所以务必确认关闭时机(例如:所有发送 goroutine 结束后)
- 无法用 range 判断 channel 是否“正在被写入”,也无法从中途停止遍历(除非用
break配合外部条件) - 带缓冲 channel 的 range 会读出所有现存值,然后阻塞等待新值或关闭;关闭后退出循环
关闭 channel 的时机和主体容易混淆,尤其在多生产者场景下 —— 它不是“谁最后发完谁关”,而是需要协调机制。别指望 runtime 帮你推断意图。









