精确控制多个协程状态
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)
- 点赞
- 收藏
- 关注作者
评论(0)