channel是goroutine间传递数据的唯一安全方式,Go强制通过channel而非共享内存实现并发通信,无缓冲channel要求收发同步,带缓冲channel按缓冲区状态阻塞,关闭后可接收零值但不可再发送。

channel 是 goroutine 间传递数据的唯一安全方式
Go 不允许通过共享内存(比如全局变量)在 goroutine 之间直接读写数据,channel 是语言层强制提供的、带同步语义的通信机制。它不是“一种可选方案”,而是唯一被 Go 运行时保障并发安全的数据传递通道。
常见错误现象:fatal error: concurrent map writes 或随机 panic,往往源于多个 goroutine 同时操作同一个 map 或结构体字段,却没加锁也没走 channel。
- 用
make(chan int)创建无缓冲 channel:发送和接收必须同步配对,否则阻塞 - 用
make(chan string, 10)创建带缓冲 channel:缓冲区满才阻塞发送,空才阻塞接收 - 不要用
chan 或 <code> 类型对外暴露 channel,除非你明确要限制读/写方向
如何避免 channel 死锁(deadlock)
死锁是最常见的 channel 错误,运行时报 fatal error: all goroutines are asleep - deadlock!,本质是至少一个 goroutine 在等没人会做的操作。
典型场景:主 goroutine 发送但没开接收 goroutine;或接收方提前退出,发送方还在发;或用 range 遍历 channel 却忘了关闭。
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 发送前确保有 goroutine 在等待接收,或 channel 有缓冲且未满
- 用
close(ch)显式关闭 channel,仅由发送方调用;接收方可通过v, ok := 判断是否已关闭 - 避免在单个 goroutine 里同时读写同一 channel(尤其无缓冲时),容易自己卡死自己
channel 关闭后还能不能发?能不能收?
关闭后的 channel 不能再发送,否则 panic:panic: send on closed channel;但可以继续接收,直到缓冲区耗尽,之后每次接收返回零值 + false。
这决定了「谁关」「何时关」必须由业务逻辑决定,不是语法自动处理的事。
- 只由负责发送的 goroutine 调用
close(ch),通常在所有数据发完后 - 接收方不依赖
ok == false来判断“数据结束”,而应配合 context 或显式信号(如 sentinel 值) - 别用
defer close(ch)在 goroutine 开头注册,可能根本没发数据就关了
用 select 处理多个 channel 时的超时和默认分支
select 是非阻塞多路复用的核心,但它的行为容易被误解:没有 case 就阻塞,有 default 就立刻执行,有 timeout 就限时等待。
性能影响:select 本身开销极小,但滥用 time.After 可能导致大量定时器堆积(尤其在循环中)。
- 用
case v := 接收;<code>case ch 发送;多个 case 同时就绪时随机选一个 - 加
default:实现非阻塞尝试,适合做“尽力而为”的投递 - 超时推荐用
time.After(5 * time.Second),但注意它会创建新定时器;高频场景改用time.NewTimer并复用
真正难的不是写对语法,而是判断哪个 goroutine 承担发送责任、谁来关 channel、要不要缓冲、要不要超时——这些都得贴着业务流设计,而不是套模板。

















