优雅关闭
妥善停止工作线程
优雅关闭 是 CoddyKit 上的免费 Go Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Go Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Go Academy 课程共包含 4 节课。
为什么需要优雅关闭
长时间运行的 Go 协程必须收到停止指令。优雅关闭可以让 worker 完成当前工作、释放资源,并在不泄漏资源或丢失数据的情况下退出。
- 不会泄漏 Go 协程
- 不会产生未完成的写入
- 进程可以干净地退出
done 通道模式
一种经典方法是使用 done 通道。关闭该通道会向所有监听它的 Go 协程广播停止信号,因为从已关闭的通道接收数据会立即返回。
done := make(chan struct{})
// ... later
close(done)使用 select 处理 Stop 信号
worker 使用 select 来选择处理作业,或注意到 done 通道的信号。当 done 通道关闭时,相应分支会触发,worker 随即返回。
for {
select {
case <-done:
return
case j := <-jobs:
process(j)
}
}context.Context
符合现代 Go 惯用方式的工具是 context。context.WithCancel 会为您提供一个上下文和一个取消函数。调用取消函数会关闭该上下文的 Done 通道。
ctx, cancel := context.WithCancel(context.Background())
defer cancel()监听 ctx.Done()
worker 会通过 ctx.Done() 进行选择。当调用取消函数或截止时间到达时,通道会关闭,worker 随即退出。
select {
case <-ctx.Done():
return ctx.Err()
case j := <-jobs:
process(j)
}等待 worker
发出停止信号还不够;您必须等待 worker 真正完成。sync.WaitGroup 会跟踪这些 worker,使 main 可以阻塞,直到计数归零。
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
runWorker(ctx)
}()
// ... later
cancel()
wg.Wait()使用上下文设置超时
context.WithTimeout 会在经过指定时长后自动取消。这限制了关闭操作在强制退出前最多可以持续的时间。
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()完整的优雅关闭
此程序运行两个 worker,取消上下文,并等待它们都干净地退出后再打印 done。
package main
import (
"context"
"fmt"
"sync"
"time"
)
func worker(ctx context.Context, id int, wg *sync.WaitGroup) {
defer wg.Done()
for {
select {
case <-ctx.Done():
fmt.Printf("worker %d stopping\n", id)
return
case <-time.After(50 * time.Millisecond):
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup
for i := 1; i <= 2; i++ {
wg.Add(1)
go worker(ctx, i, &wg)
}
time.Sleep(120 * time.Millisecond)
cancel()
wg.Wait()
fmt.Println("all workers stopped")
}排空正在执行的工作
关闭时,您通常希望 worker 完成已经开始的工作。可以在最后的循环中读取剩余作业,或者停止接受新作业,同时让当前作业完成。
捕获 OS 信号
在实际服务器中,signal.NotifyContext 会将取消操作与 SIGINT 和 SIGTERM 关联起来。按下 Ctrl+C 后,就会触发与手动取消相同的优雅关闭流程。
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()切勿忘记 cancel()
即使使用 WithTimeout,也始终要使用 defer cancel()。如果不调用 cancel,上下文内部的 Go 协程和计时器会一直泄漏,直到截止时间触发。
快速检查
请测试您对关闭流程的理解。
回顾
您已学会优雅关闭:
- 使用 context.WithCancel 或 done 通道发出停止信号
- worker 通过 ctx.Done() 进行选择
- WaitGroup 确保所有 worker 都真正退出
- 在实际服务器中,将取消操作与 OS 信号关联
- 始终使用 defer cancel()
用 AI 导师学习 Go — 免费
在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。
- 课程
- 51
- 课程
- 203
常见问题解答
「优雅关闭」课时是免费的吗?
是的 — 「优雅关闭」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Go Academy 课程的其余内容,请升级到 CoddyKit PRO。 Go Academy 课程共包含 4 节课。
「优雅关闭」这节课中我会学到什么?
妥善停止工作线程 你通过在浏览器中直接运行的动手代码来练习 Go Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Go Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Go Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「优雅关闭」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Go Academy 课中编写并运行代码吗?
能。每节 Go Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。