channel是goroutine间安全传递数据的同步原语;需用make初始化,nil channel操作会deadlock;无缓冲需收发双方同时就绪,有缓冲按容量控制阻塞;应避免直接对var声明的channel收发。

Go 的 channel 不是“线程间通信”工具——Go 没有线程,只有 goroutine;channel 是 goroutine 之间**安全传递数据的同步原语**,不是共享内存的替代品,也不是消息队列。
channel 基本声明与初始化必须显式指定类型和容量
声明 chan int 只是类型,不等于可用;未初始化的 channel 是 nil,向其发送或接收会永久阻塞(deadlock)。必须用 make 初始化:
ch := make(chan int) // 无缓冲,同步阻塞 ch := make(chan string, 10) // 有缓冲,容量为 10
- 无缓冲 channel:发送和接收必须**同时就绪**,否则阻塞;适合严格同步场景(如等待任务完成)
- 有缓冲 channel:发送时若未满则立即返回,接收时若非空则立即返回;缓冲区满/空才阻塞
- 切勿用
var ch chan int后直接 send/receive——这是常见 panic 来源
select 配合 channel 实现非阻塞收发与超时控制
单个 <-ch 会阻塞,实际业务中常需避免无限等待。用 select + default 或 time.After 实现:
select {
case msg := <-ch:
fmt.Println("received:", msg)
default:
fmt.Println("no message available")
}-
default分支让 select 变成**非阻塞轮询**,但频繁轮询浪费 CPU,慎用 - 加超时:把
time.After(1 * time.Second)当作一个 channel 放进 select,可优雅退出等待 - 多个 channel 同时监听时,
select随机选择一个就绪分支(不是 FIFO),不能依赖顺序 - 一个 channel 不能在同一个 select 中既读又写(语法错误)
关闭 channel 后只能接收,不能发送,且接收会返回零值+ok=false
关闭 channel 表示“不再发送”,但接收方仍可继续读取剩余数据。关闭已关闭的 channel 会 panic;向已关闭的 channel 发送会 panic;从已关闭的 channel 接收不会 panic:
立即学习“go语言免费学习笔记(深入)”;
close(ch) // 正确:显式关闭 ch <- 1 // panic: send on closed channel v, ok := <-ch // ok == false 当且仅当 channel 已关闭且无剩余数据
- 只应由**发送方**关闭 channel;接收方关闭是逻辑错误
- 用
v, ok := 判断是否关闭,而不是靠 <code>range就认为一定安全——如果 channel 在 range 过程中被关闭,range 会自动退出,但你可能需要处理最后一批数据 - 不要用 close 作为“信号”来通知接收方停止——应该用额外的 done channel 或 context
channel 泄漏与死锁是最常见的并发陷阱
goroutine 启动后若因 channel 阻塞无法退出,就会泄漏;多个 goroutine 相互等待对方收/发,就会 deadlock。典型场景:
- 向无缓冲 channel 发送,但没有 goroutine 在另一端接收——主 goroutine 卡住,程序 panic
- 启动 goroutine 写 channel,但主 goroutine 忘记读、也没关闭、还没加超时——写 goroutine 永远阻塞
- 用
range ch读取,但发送方忘记 close,且无其他退出机制——接收方永远等下去 - 多个 channel 组合使用时,某条路径没覆盖所有 case,导致部分 goroutine 被挂起
调试时加 go tool trace 或用 runtime.NumGoroutine() 观察数量异常增长,比靠日志更早发现问题。










