go 守护进程若未正确处理退出信号,即使服务被停止,goroutine 仍会持续运行。本文详解如何通过上下文(context)、通道(channel)和信号监听实现优雅关停,避免资源泄漏与后台残留。
go 守护进程若未正确处理退出信号,即使服务被停止,goroutine 仍会持续运行。本文详解如何通过上下文(context)、通道(channel)和信号监听实现优雅关停,避免资源泄漏与后台残留。
在 Go 中编写守护进程(daemon)时,一个常见误区是使用 select {} 无限阻塞主 goroutine,以“防止程序退出”——但这恰恰导致进程无法响应停止指令,goroutine 持续运行(如每 10 秒创建文件),即使系统已执行 systemctl stop 或 kill 命令。
根本问题在于:原代码中 case "run": 分支启动所有队列处理 goroutine 后,仅靠 select {} 阻塞主线程,既未监听系统信号(如 SIGINT/SIGTERM),也未向工作 goroutine 传递停止通知,导致它们永远运行。
✅ 正确做法是引入可取消的 context 和 退出通道,实现双向协同关停:
1. 使用 context.WithCancel 管理生命周期
func runDaemon(queues []QueueConfig) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // 确保退出时清理
// 监听系统终止信号
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigChan
log.Println("Received shutdown signal, stopping...")
cancel() // 触发所有派生 context 取消
}()
// 并行启动队列处理器,每个都接收 ctx.Done()
var wg sync.WaitGroup
for _, q := range queues {
wg.Add(1)
go func(queue QueueConfig) {
defer wg.Done()
startProcessing(ctx, queue) // 修改函数签名,接受 ctx
}(q)
}
// 等待所有 goroutine 自然退出
wg.Wait()
log.Println("All workers stopped gracefully.")
}2. 改造 startProcessing 以响应取消
func startProcessing(ctx context.Context, q QueueConfig) {
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
count := 0
maxProcesses := q.MaxProcesses
for {
select {
case <-ctx.Done():
log.Printf("Stopping processor for queue %s: %v", q.Name, ctx.Err())
return // 立即退出
case <-ticker.C:
count++
if count > maxProcesses {
return
}
createTestFile(q.Name, count) // 示例业务逻辑
}
}
}3. 关键注意事项
- ❌ 避免 select {} —— 它使主 goroutine 不可中断,违背 Go 的并发哲学;
- ✅ 主 goroutine 应负责协调(监听信号、启动 worker、等待完成),而非直接执行业务;
- ✅ 所有长期运行的 goroutine 必须定期检查 ctx.Done() 或接收退出通道信号;
- ✅ 使用 sync.WaitGroup 确保所有 worker 完全退出后再结束主流程;
- ✅ 在 service.Run() 实现中(如 github.com/kardianos/service),Execute 方法应返回以释放服务管理器资源。
通过上述改造,当系统发送 SIGTERM(如 systemctl stop myapp)时,程序将:
- 立即捕获信号并调用 cancel();
- 所有 startProcessing goroutine 在下次 select 时检测到 ctx.Done() 并退出;
- 主 goroutine 等待全部 worker 结束后自然返回,进程彻底终止。
这才是符合 Go 习惯的、可维护的守护进程设计模式。

















