go并发控制靠channel、sync.mutex、sync.waitgroup和context.context四类原语轻量约束;channel用于协作/流控,mutex仅保护共享变量读写;pool需重置状态且易致内存泄漏,高频小结构体宜直接分配,大buffer用bytes.buffer.reset()更可控;稳定性取决于资源释放时机、清理责任及超时取消是否穿透到底层i/o。
Go 的并发控制不是靠“设计大架构”实现的,而是靠组合 channel、sync.Mutex、sync.WaitGroup 和 context.Context 这四类原语,在关键路径上做轻量、明确的约束。
什么时候该用 channel 而不是 sync.Mutex
核心判断标准:是否涉及「协作」或「流控」。仅保护共享变量读写,用 sync.Mutex 更直接;若需协调 goroutine 启停、传递信号、限速、背压(如生产者-消费者),必须用 channel。
-
channel天然带同步语义,ch 会阻塞直到有接收方,这是 <code>Mutex永远做不到的 - 关闭
channel是向所有接收方广播“结束”的唯一安全方式,别用bool变量模拟 - 避免在循环里反复创建无缓冲
channel——这会频繁触发调度器,性能比Mutex差一个数量级 - 高频小数据通信(如每毫秒一次状态更新),优先考虑
atomic.Value+sync.RWMutex,而非chan struct{}
context.WithTimeout 必须和 select 配合使用才生效
context.WithTimeout 本身不中断任何 goroutine,它只让 ctx.Done() 返回的 channel 在超时后可被接收。不写 select,超时就完全没意义。
- 常见错误:启动 goroutine 后丢弃
ctx,或只在入口处检查ctx.Err()但未在 I/O 或循环中持续监听 - HTTP handler 中,
ctx来自 request,应透传给下游调用(如数据库查询、RPC),且每个阻塞操作都应参与select - 不要用
time.After替代ctx.Done()——前者无法被取消,会泄漏 timer - 子 context(如
WithCancel)的 cancel 函数必须显式调用,且只能调用一次;重复调用 panic,漏调则资源泄漏
高并发下 sync.WaitGroup 的典型误用场景
WaitGroup 本质是计数器,不是 goroutine 生命周期管理器。它只保证“所有 Add 的数量都被 Done”,不保证 goroutine 已结束或未 panic。