1.6 KiB
1.6 KiB
并发规范
Go 后端并发约定。
- 每个 goroutine 必须有明确退出路径:外部用
context.Context或 stop channel 通知,内部select响应;禁止 fire-and-forget,不允许 goroutine 泄漏。 - 调用方能等待 goroutine 退出:
sync.WaitGroup或donechannel。 init()中不启动 goroutine、不做 IO。- channel 容量只用 0 或 1,更大缓冲须注释说明理由。
- 涉及阻塞或远程调用的函数第一个参数是
ctx context.Context,不把 ctx 存进 struct。
模式:服务级后台 goroutine 的落地形态(2026-07 起为既定写法)
面板内「异步旁路」(通知发送 Notifier.SendAsync、日志异步落库 SystemLogService.Record、定期清理 StartCleanup)统一三件套:
s.wg.Add(1)
go func() {
defer s.wg.Done()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) // 自带超时,不复用请求 ctx
defer cancel()
if err := s.doWork(ctx); err != nil {
log.Printf("xxx: %v", err) // 旁路失败只记日志,绝不影响主流程
}
}()
- 服务暴露
Wait()(内部wg.Wait());常驻循环(如清理 ticker)额外接收 ctx,select响应退出。 - cmd/server 装配的 defer 顺序必须是「先停生产者、再等消费者」(LIFO):
defer notifier.Wait()/defer systemLogs.Wait()写在defer tasks.Stop()/defer stopCleanup()之前。 - 陷阱:
cron.Stop()返回的 context 要等(<-s.cron.Stop().Done()),否则仍在执行的任务尚未wg.Add时Wait()会提前返回,goroutine 泄漏且违反 WaitGroup 的 Add/Wait happens-before 约束。