Go Academy · 课时

优雅关闭

妥善停止工作线程

第 4 / 4 课13 个步骤

优雅关闭 是 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 工作池模式
  2. 扇出与扇入
  3. 处理流程阶段
  4. 优雅关闭
← 返回 Go Academy