无缓冲通道强制发送与接收同步就绪,否则任一端永久阻塞,易导致死锁;正确用法需协程并发配合或改用缓冲通道。

chan 不是“异步语言特性”,而是 Go 并发模型的通信原语。它本身不提供异步/await 那类语法糖,也不自动调度任务——它的行为完全由缓冲区容量、goroutine 生命周期和阻塞规则决定。想靠它写出健壮异步逻辑,关键不是模仿其他语言,而是理解它怎么“卡住”和“放行”。
无缓冲通道 make(chan int) 的同步陷阱
它强制发送方和接收方“面对面交接”,任一方先到就得等。这在需要精确时序控制时很可靠,但极易导致死锁。
- 常见错误:主 goroutine 启动多个生产者后,只做一次
接收,其余发送永远阻塞,程序 hang 住 - 典型场景:主协程等待子协程完成初始化(如配置加载完毕、DB 连接就绪),用
done := make(chan struct{})通知最稳妥 - 别把它当队列用——它不存数据,只协调时机;想缓冲数据,必须显式指定容量
带缓冲通道 make(chan int, N) 的容量选择逻辑
缓冲区大小不是拍脑袋定的,它直接决定背压行为和内存开销。
- 设为 0 → 退化成无缓冲通道;设为 1 → 允许“发完就走”,但不保证接收端已处理;设为 >1 → 可暂存多个待处理项
- 若生产速度远高于消费速度,缓冲区会快速填满,后续
ch 就会阻塞,这其实是反压机制在起作用,不是 bug - 线上服务中,
make(chan Request, 100)比make(chan Request, 10000)更安全:前者内存可控,后者可能 OOM 或掩盖消费瓶颈
关闭通道 close(ch) 和 range ch 的配合边界
关闭只应由发送方执行,且只能关一次;接收方用 range 遍历的前提是通道终将被关闭。
立即学习“go语言免费学习笔记(深入)”;
- 错误示范:多个 goroutine 同时调用
close(ch)→ panic: close of closed channel - 正确模式:用
sync.WaitGroup等待所有生产者退出后,由主 goroutine 调用close(ch) -
range ch仅适用于“单写多读”或“明确知道谁负责关闭”的场景;若存在动态增删 worker,应改用select+ok := 判断是否关闭
通道方向类型 chan 和 <code> 的实际约束力
它们不是文档注释,是编译器强制检查的类型契约,能提前暴露设计错误。
- 函数参数声明
func worker(jobs ,调用方传入双向通道会被自动转换,但 worker 内部无法对 <code>jobs执行发送、也无法对results执行接收 - 这能防止误操作:比如本该只读的通道被意外写入,编译失败比运行时 panic 更早发现问题
- 不要为了“省事”在函数内把
强转回 <code>chan int——那等于绕过类型系统,失去保护意义
通道的复杂点不在语法,而在责任划分:谁创建、谁关闭、谁读、谁写、缓冲区是否足够、goroutine 是否泄漏。这些决策一旦出错,问题往往延迟暴露,且难以复现。写的时候多问一句“这个 ch <- 如果没人收,会卡在哪?”比记住所有规则更管用。


















