精确控制多个协程状态

举报
码乐 发表于 2026/01/31 08:03:24 2026/01/31
【摘要】 1 简介本文简介如何在go语言程序精确控制某一个协程状态golang如何控制协程goroutine? 使其在某个信号到达时,某个协程就执行某个动作比如退出,不是全部协程都退出。本文后面按 3 种常见且靠谱的方式,从推荐到简单,慢慢讲清楚。 2 推荐方案一 :独立 context + 定向 cancel每个需要被单独控制的 goroutine,用自己的 context示例:只退出 worke...

1 简介

本文简介如何在go语言程序精确控制某一个协程状态

golang如何控制协程goroutine? 使其在某个信号到达时,某个协程就执行某个动作比如退出,不是全部协程都退出。

本文后面按 3 种常见且靠谱的方式,从推荐到简单,慢慢讲清楚。

2 推荐方案一 :独立 context + 定向 cancel

每个需要被单独控制的 goroutine,用自己的 context

示例:只退出 worker1,不影响 worker2

    package main

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

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

    func main() {
        rootCtx := context.Background()

        ctx1, cancel1 := context.WithCancel(rootCtx)
        ctx2, _ := context.WithCancel(rootCtx)

        go worker(ctx1, "worker1")
        go worker(ctx2, "worker2")

        sig := make(chan os.Signal, 1)
        signal.Notify(sig, syscall.SIGUSR1)

        <-sig // 收到信号
        fmt.Println("SIGUSR1 received, stop worker1")

        cancel1() // 只退出 worker1

        time.Sleep(5 * time.Second)
    }

** 关键点

不要共用同一个 context

cancel 哪个,就只影响哪个 goroutine

非常适合「任务级 / 连接级」控制

3 最直观方案二:专属退出 channel

给每个 goroutine 一个 stop channel

  func worker(stop chan struct{}, name string) {
      for {
          select {
          case <-stop:
              fmt.Println(name, "exit")
              return
          default:
              time.Sleep(time.Second)
          }
      }
  }

  func main() {
      stop1 := make(chan struct{})
      stop2 := make(chan struct{})

      go worker(stop1, "worker1")
      go worker(stop2, "worker2")

      sig := make(chan os.Signal, 1)
      signal.Notify(sig, syscall.SIGUSR1)

      <-sig
      close(stop1) // 只关 worker1
  }

** 适合

简单工具

不需要 timeout / deadline

goroutine 生命周期短

4 方案三:信号 → 分发器 → 指定 goroutine

当你有 N 个协程,需要根据不同信号/条件精确控制

    type ControlMsg struct {
        Target string
    }

    func worker(name string, ctrl <-chan ControlMsg) {
        for {
            select {
            case msg := <-ctrl:
                if msg.Target == name {
                    fmt.Println(name, "exit")
                    return
                }
            default:
                time.Sleep(time.Second)
            }
        }
    }

    ctrl := make(chan ControlMsg)

    go worker("A", ctrl)
    go worker("B", ctrl)

    ctrl <- ControlMsg{Target: "A"} // 只退出 A

适合 调度器 / 任务池

❌ 错误示例(很常见)
❌ 所有 goroutine 用同一个 context

	ctx, cancel := context.WithCancel(context.Background())


	cancel 一次,全死 ❌

❌ 用 signal 直接在 worker 里监听

	signal.Notify(...)

信号只能被一个 goroutine 正确消费

极易混乱

5 小结

设计建议

场景 单个任务取消 推荐 独立 context

场景 批量任务 推荐 parent ctx + child ctx
场景 worker pool 推荐control channel
场景 服务级退出 推荐root ctx
场景实战最佳实践 推荐组合拳 :

rootCtx := signal.NotifyContext(context.Background(), syscall.SIGTERM)

taskCtx, cancelTask := context.WithCancel(rootCtx)

想让「某个 goroutine」在信号到来时退出,而不是全部退出,核心思路是:
给“这个协程”一个“专属的退出信号”(而不是用全局 cancel)

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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