Go Worker Pool 关闭:让任务停止有边界、结果有去处
背景 关闭任务队列之前如果仍有提交者写入,会发生 send on closed channel。关闭一次和关闭安全不是同一件事。 核心判断 先通过统一入口停止提交,再等待所有提交者退出。 唯一协调者关闭 jobs,worker 遍历队列排空后退出。 最后等待 worker;任务本身仍应遵守超时与取消。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 // 前提:统一入口已拒绝新提交,所有可能发送 jobs 的协程都已退出。 // 仅由协调者调用,不从 worker 内调用。 func (p *Pool) drain() { p.once.Do(func() { close(p.jobs) p.wg.Wait() }) } func (p *Pool) worker(ctx context.Context) { defer p.wg.Done() for job := range p.jobs { job.Run(ctx) } } 边界与误区 sync.Once 只能防止重复 close,不能防止并发 send。示例的 drain 要在提交者已经退出后由协调者调用,不得从 worker 自身调用以免等待自己。 验证方式 用竞态检测测试提交与停止交错,再验证空队列、阻塞任务和重复停止。明确关闭是排空任务还是丢弃任务,并对未完成结果提供可追踪状态。 小结 关闭时先禁止新提交,再关闭队列并等待 worker,任务自身要响应 context。