一次性控制多个协程状态

举报
码乐 发表于 2026/01/30 22:35:48 2026/01/30
【摘要】 1 简介控制协程全部退出,控制协程goroutine,使其在某个信号到底时协程就退出在 Go 里,最正宗、最通用、也是官方推荐的方式是:context + signal。本文按「正确姿势 → 常见写法 → 易踩坑」慢慢讲清楚。 2 推荐方案:context.WithCancel + os/signal基本思路,主协程监听系统信号(如 SIGINT / SIGTERM)收到信号后 canc...

1 简介

控制协程全部退出,控制协程goroutine,使其在某个信号到底时协程就退出

在 Go 里,最正宗、最通用、也是官方推荐的方式是:context + signal。

本文按「正确姿势 → 常见写法 → 易踩坑」慢慢讲清楚。

2 推荐方案:context.WithCancel + os/signal

基本思路,主协程监听系统信号(如 SIGINT / SIGTERM)

收到信号后 cancel()

goroutine 里 select 监听 ctx.Done(),主动退出

强烈推荐代码 标准示例

    package main

    import (
        "context"
        "fmt"
        "os"
        "os/signal"
        "syscall"
        "time"
    )

    func worker(ctx context.Context) {
        for {
            select {
            case <-ctx.Done():
                fmt.Println("worker exit")
                return
            default:
                fmt.Println("working...")
                time.Sleep(time.Second)
            }
        }
    }

    func main() {
        ctx, cancel := context.WithCancel(context.Background())

        go worker(ctx)

        // 监听信号
        sigCh := make(chan os.Signal, 1)
        signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)

        <-sigCh // 阻塞,直到收到信号
        fmt.Println("receive signal, exiting...")

        cancel()               // 通知所有 goroutine
        time.Sleep(time.Second) // 等待 goroutine 优雅退出
    }
  • 优点

Go 官方推荐

可层层传递(HTTP / RPC / DB 都支持)

支持 timeout / deadline

非常适合微服务

3 更优雅:signal.NotifyContext(Go 1.16+)

这是现在最爽的写法

  ctx, stop := signal.NotifyContext(
      context.Background(),
      syscall.SIGINT,
      syscall.SIGTERM,
  )
  defer stop()

  go worker(ctx)

  <-ctx.Done()
  fmt.Println("main exit")

收到信号 ⇒ ctx.Done() 自动关闭
不用自己写 channel + signal.Notify

多个 goroutine 一起退出

  for i := 0; i < 3; i++ {
      go func(id int) {
          for {
              select {
              case <-ctx.Done():
                  fmt.Printf("worker %d exit\n", id)
                  return
              default:
                  time.Sleep(time.Second)
              }
          }
      }(i)
  }

一个 cancel,全部退出

常见坑的 不推荐方案

错误:用 bool 标志位

		var stop bool

缺点:

数据竞争

不可组合

不可传递

强杀 goroutine

Go 不支持从外部 kill goroutine
必须靠 goroutine 自己退出

4 适合简单场景的channel 控制

  stop := make(chan struct{})

  go func() {
      <-stop
      fmt.Println("exit")
  }()

  close(stop)

不支持 timeout / deadline / 层级控制

5 小结

信号一来 → cancel context → 所有 goroutine 自己优雅退出。

实战建议

  场景				建议

  服务 / 守护进程			context + signal
  HTTP / gRPC			直接用 request.Context
  简单工具			signal.NotifyContext
  多层调用			context 一路往下传
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。