Go 为什么宁可让 chansend 卡住,也不让你误以为 ch <- v 发成功了
ch <- v 在 Go runtime 里不是发往一个队列就完事了。chansend 有五条路径:先看有没有 receiver,再看 buffer 有没有空位,最后才决定是挂起还是直接失败。每一条路径背后都藏着一个取舍。
ch <- v 在 Go runtime 里不是发往一个队列就完事了。chansend 有五条路径:先看有没有 receiver,再看 buffer 有没有空位,最后才决定是挂起还是直接失败。每一条路径背后都藏着一个取舍。
很多 Go 代码把 context.WithValue 当成万能参数袋,函数签名看起来干净,依赖却被藏进调用链。从 valueCtx、propagateCancel、Background 和 TODO 的源码边界出发,给出一份可以直接拿去做 code review 的 context 检查清单。
很多 Go 代码写了 context.WithTimeout,却仍然出现 goroutine 上涨和资源保留。问题不在 timeout 没生效,而在 cancel 的职责、Done 的响应和排查路径被混在了一起。
线上接口超时后 goroutine 数还在上涨,很多人第一反应是 context 没生效。真正的问题往往不是 timeout 没到,而是业务 goroutine 没有响应取消信号。
Go channel 写到真实工程里,最容易出问题的往往不是 send 和 receive,而是 close、select、buffer 和生命周期。close 是发送端协议,buffer 不是取消机制,channel 一旦出现,就要设计谁创建、谁发送、谁关闭、谁退出。
Go 并发代码里,channel 和 mutex 不是谁更高级的问题。mutex 保护一块状态,channel 描述一段关系。下次纠结工具前,先用这五个问题判断问题形状。
Go channel 的价值不是替代 mutex,也不是把锁藏起来,而是把 goroutine 之间的通信关系写清楚。一个简单 cache 被 channel 化,往往不是更 Go,而是把一把锁绕成了一套协议。
很多 Go map 文章还在讲 hmap、bmap、overflow bucket 和 load factor 6.5。但从 Go 1.24 开始,默认 map 实现已经换成 Swiss Table。API 没变,底层发动机变了。
很多人能背出 G 是 goroutine、M 是线程、P 是 processor,却讲不清 P 为什么存在。理解 Go 调度器,关键不是记住三个字母,而是看懂 runtime 为什么要把任务、线程和执行资源拆开。
Go 数组不是 slice 的过时底层细节。它把长度、值语义、比较能力和固定布局都写进类型里,牺牲弹性,换来边界清楚和编译器可证明的信息。