<p>ch := make(chan int) 一运行就死锁,是因为无缓冲通道要求发送与接收操作必须同步就绪;单 goroutine 中先发送后接收会导致发送永久阻塞,触发“all goroutines are asleep - deadlock”。</p>

Go 的 Channel 不是“学完语法就能用好”的东西,它必须和 Goroutine 的生命周期、退出逻辑、错误传播一起设计,否则很快就会卡死或 panic。
为什么 ch := make(chan int) 一跑就 deadlock?
无缓冲通道要求发送和接收必须“同时发生”。如果只启动一个 Goroutine 发送 ch ,而主线程还没走到 <code>,发送方就会永远阻塞——Go 运行时检测到所有 Goroutine 都在等,直接 panic "deadlock"。
- 常见写法错误:漏掉
go关键字,让发送/接收在同一个 Goroutine 里串行执行(比如先ch 再 <code>),这等于没并发,还白占一个 goroutine - 典型陷阱:用
for range ch遍历一个没人 close 的 channel,循环永远不退出 - 调试提示:加
runtime.GOMAXPROCS(1)能更快暴露调度顺序问题,但不是修复手段
make(chan int, N) 的缓冲区大小怎么选?
缓冲区不是越大越好。它本质是解耦生产与消费节奏的“临时仓库”,但会掩盖背压缺失问题。
- 设为 0:严格同步,适合信号通知(如
done := make(chan struct{}))、配对操作 - 设为 1:常用作“至少一次交付”标志,比如任务完成确认
done - 设为 >1:仅当明确知道最大积压量且能承受内存开销时才用,比如批量日志暂存;超过后仍会阻塞,和无缓冲行为一致
- 性能影响:缓冲区越大,
hchan结构体中buf指向的堆内存越多,GC 压力上升
关闭 channel 的时机和方式不对,会 panic 或丢数据
close(ch) 只能由发送方调用,且只能调一次。接收方靠 val, ok := 中的 <code>ok 判断是否已关闭——这不是可选技巧,而是唯一安全读取方式。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
立即学习“go语言免费学习笔记(深入)”;
- 错误做法:多个 Goroutine 同时 close 同一个 channel → panic "close of closed channel"
- 危险模式:用
for range ch但没确保只有发送方能 close → 可能提前退出,漏掉后续数据 - 推荐组合:用
sync.WaitGroup等待所有发送 Goroutine 结束,再 close;接收方用for range或带ok的单次接收 - 特别注意:
nilchannel 永远阻塞,close(nil)直接 panic,所以初始化检查不能省
单向 channel(chan / <code>)不是语法糖,是接口契约
声明函数参数为 chan,意味着调用者必须传一个“只允许发送”的 channel,编译器会阻止你在函数内做 <code> 操作——这是静态检查,不是运行时约束。
- 实际价值:强制分离职责。比如工作池中,分发任务的函数只接受
chan,消费者只接受 <code>,天然防误用 - 转换规则:双向 channel 可隐式转为单向,但单向不能转回双向;
make(chan int)返回的是双向,需显式转型才能传给单向参数 - 容易忽略点:返回单向 channel 的函数,调用方若想复用该 channel 接收数据,必须在创建时就保留双向引用,否则无法“降级”使用
Channel 的真正复杂点不在语法,而在它把“谁负责关、谁负责读、谁负责处理错误”全部绑在一起。哪怕只改一行 close 位置,整个流程的可靠性就可能崩掉——这种耦合性,恰恰是 Go 并发模型最不容跳过的逻辑闭环。

















