发布于2026-05-21 阅读(0)
扫一扫,手机访问
直接甩一堆 go f() 去启动并发任务?大概率会出问题——语法上没错,但系统资源很容易失控。内存暴涨、下游服务返回429、runtime: out of memory 或者满屏的 context.DeadlineExceeded 错误,都是常见后果。更头疼的是,日志里往往找不到到底是哪批任务捅的篓子。

semaphore.Weighted 控制最大并发数别自己手写计数器或者用 sync.Mutex 硬扛了。官方库 golang.org/x/sync/semaphore 提供的 Weighted 信号量,天然支持带 context 的获取和超时机制,用起来更安全可靠。
sem := semaphore.NewWeighted(8) 这行代码,就限定了最多只能有8个任务同时执行。sem.Acquire(ctx, 1) 拿到“通行证”。如果获取失败(比如超时或被取消),任务就该跳过或安排重试。defer sem.Release(1) 必须成对出现,而且务必放在 defer 里——这是确保即使任务 panic 了,资源也能被释放的唯一合理位置。Acquire 得放在 goroutine 内部调用。如果放在外面,那就退化成串行执行了,失去了并发的意义。chan Task + worker pool 实现排队与复用当突发流量远超系统的瞬时处理能力时,你需要一个缓冲区来暂存请求,避免调用方被阻塞或者请求被直接丢弃。这就是 worker pool 模式的用武之地。
type Task struct { ID string; Fn func() }。任务输入通道建议带上缓冲:jobs := make(chan Task, 100)。for i := 0; i select 语句可以防止生产者被无限阻塞:select { case jobs ctx.Done(),尤其是在执行 HTTP 请求、数据库查询这类可能阻塞的操作时,以便及时响应取消信号。close(jobs) 来通知 worker 们优雅退出,否则 for range jobs 这个循环会永远等下去。errgroup.Group 统一处理错误与取消sync.WaitGroup 只管等待任务完成,不处理错误。而 errgroup.Group 则更进一步,它天然支持“一个出错,全体取消”的语义,并且能自动与 context 进行集成。
g, ctx := errgroup.WithContext(context.WithTimeout(context.Background(), 30*time.Second)),这样所有任务都共享一个带超时的上下文。g.Go(func() error { return process(ctx, task) }),无需再手动调用 wg.Add 和 wg.Done。if err := g.Wait(); err != nil,它会返回第一个非 nil 的错误。ctx.Err()。例如,发起 HTTP 请求时应该使用 http.NewRequestWithContext(ctx, ...)。g.Go 再套进另一个裸的 go 语句里,因为它并不会递归地管理你内部启动的子 goroutine。goroutine 的执行完成顺序是不确定的,所以不能指望它们按启动顺序把结果写进同一个 slice。另外,闭包捕获循环变量 i 是个经典的高频翻车点。
type Result struct { Index int; Data interface{}; Err error }。results := make(chan Result, len(tasks))。results len(tasks)),然后根据结果中的 Index 字段,将结果填回到最终的结果切片中,这样就保证了顺序。go func(idx int, task Task) { ... }(i, task),而不是在闭包内部直接引用外部循环变量 i。说到底,在 Go 里实现并发,真正难的不是“怎么让代码跑起来”,而是如何精细地控制“谁该先跑、能跑多久、失败了怎么通知队友、超时了如何优雅收尾”。这些细节,但凡漏掉一个,很可能就在某个凌晨三点的压测中,变成刺耳的告警铃声。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8