go 的 chan 默认是无缓冲的,发送时若无人接收则阻塞。

channel 发送时阻塞,是因为没人在接收
Go 的 chan 默认是无缓冲的,ch 这一操作会一直卡住,直到有 goroutine 在另一端执行 <code>。这不是 bug,是设计使然——它天然支持协程间同步。
- 常见错误现象:
fatal error: all goroutines are asleep - deadlock,通常就是主 goroutine 发完数据后没启接收者,或接收者启了但没真正读 - 使用场景:想等某个任务完成再继续(比如初始化加载完配置才启动 HTTP server),用无缓冲 channel 最直接
- 如果不想阻塞,要么用带缓冲的
make(chan int, 1),要么把发送逻辑包进 goroutine:go func() { ch - 注意:缓冲容量只影响“能存几条未被取走的数据”,不改变语义;缓冲为 0 和不写数字效果一样
从 nil channel 读写会永久阻塞
var ch chan int 声明但未 make,这个 ch 是 nil。对它发或收,当前 goroutine 就彻底卡死,且无法被 select 检测到超时(除非整个程序只有它一个 goroutine,才会触发 deadlock 报错)。
- 容易踩的坑:函数参数传入
chan,但调用方传了nil;或者 channel 初始化逻辑被 if 分支跳过,导致后续读写落在nil上 - 安全做法:接收方先判空 —— 虽然 Go 不允许
ch == nil这样比,但可以用if ch == nil(合法),或更稳妥地在函数入口加if ch == nil { panic("ch is nil") } - select 中如果某个 case 是
nilchannel,那该 case 永远不会被选中,相当于“禁用”了那个分支
close() 后还能读,不能写
关闭 channel 只是告诉接收方“不会再有新数据了”,不是销毁它。关闭后继续 ch 会 panic:<code>panic: send on closed channel;但 仍可执行,只是会立即返回零值 + <code>false(第二个返回值表示是否还有数据)。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 典型误用:在 for-range 循环里手动 close —— for-range 本身会在 channel 关闭后自动退出,不需要、也不应该在循环体里 close
- 谁负责 close?一般是发送方。多个 goroutine 同时发时,必须确保只有一个 close,否则 panic;可用 sync.Once 或由协调 goroutine 统一关
- 接收方判断是否结束,优先用 for-range;若需单次读取并判断,用
v, ok := ,ok 为 false 表示已关闭且无剩余数据
select 配合 default 会变成非阻塞操作
单独写 select { case 是阻塞的;加上 <code>default 后,它就退化成“尝试读一次,有数据就读,没数据就立刻走 default”,类似轮询。
- 性能影响:频繁空转的
select {... default:}会吃 CPU,尤其在 tight loop 里;真需要非阻塞,应结合 time.After 或 context.WithTimeout 控制重试节奏 - 常见用途:实现“带超时的发送”或“尽力而为的接收”,比如日志上报通道满时降级写本地文件
- 注意:default 不代表“默认分支”,它只是个立即可执行的 case;如果其他 case 都 ready,default 依然不会被选中
channel 的行为高度依赖运行时调度和 goroutine 状态,很多问题表面看是语法错,实际是控制流没理清。多打几个 fmt.Println 观察 goroutine 生命周期,比查文档更快定位问题。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










