Golang Windows服务优雅停机问题求助:等待任务完成再停止
Golang Windows服务停止问题解决方案
问题1:time.sleep被中断的原因及修复
time.sleep被中断是因为http请求的上下文会在连接关闭时被取消——如果你的底层任务依赖请求上下文(比如r.Context()),当服务触发停止、http.Server开始关闭连接后,活跃请求的上下文会被取消,导致依赖该上下文的sleep或任务被中断。另外,直接用time.sleep等待任务完成本身就不可靠,应该用sync.WaitGroup跟踪所有正在执行的底层任务。
修复步骤:
- 在服务结构体中添加
sync.WaitGroup,用于统计正在运行的任务数 - 在每个请求处理函数的开头调用
wg.Add(1),任务结束时调用wg.Done() - 停止服务时,先调用
http.Server.Shutdown()关闭监听,再调用wg.Wait()等待所有任务完成,替代time.sleep
示例代码片段:
type webService struct { server *http.Server wg sync.WaitGroup } // 请求处理函数示例 func (ws *webService) handleRequest(w http.ResponseWriter, r *http.Request) { ws.wg.Add(1) defer ws.wg.Done() // 模拟耗时业务任务 select { case <-time.After(5 * time.Second): fmt.Fprintf(w, "任务完成") case <-r.Context().Done(): // 可选:处理上下文取消的场景 return } }
问题2:刚启动停止时Windows报错的修复
Windows服务要求收到停止命令后,必须立即反馈"正在停止"的状态,否则系统会判定服务无响应并抛出报错。需要在case svc.Stop, svc.Shutdown:分支开头添加状态报告,告知Windows服务正在处理停止请求,最后在停止完成后报告最终状态。
修复代码片段:
func (ws *webService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) { const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown changes <- svc.Status{State: svc.StartPending} // 启动http服务的逻辑... changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted} for { select { case c := <-r: switch c.Cmd { case svc.Stop, svc.Shutdown: // 1. 向Windows报告服务进入停止待处理状态 changes <- svc.Status{State: svc.StopPending, Accepts: cmdsAccepted} // 2. 关闭http服务监听,不再接收新请求 err := ws.server.Shutdown(context.Background()) if err != nil { // 处理关闭错误 } // 3. 等待所有底层任务执行完毕 ws.wg.Wait() // 4. 向Windows报告服务已停止 changes <- svc.Status{State: svc.Stopped} return // 其他命令的处理逻辑... } } } }
额外注意事项
- 所有耗时任务(包括请求结束后异步启动的goroutine)都必须被
WaitGroup跟踪,避免遗漏 http.Server.Shutdown()会自动等待活跃连接处理完成(可传入带超时的上下文控制等待时长),但异步任务必须单独用WaitGroup管理- 禁止用time.sleep替代
WaitGroup,固定等待时间无法保证任务真的完成
内容的提问来源于stack exchange,提问作者user1956194
相关产品推荐
相关产品推荐

