You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用sync.WaitGroup时有没有更优的for循环退出方案?

问题分析
  • 你原来直接在orders >= quantity判断块内跳出循环会报错的核心原因:已经执行了sem <- true占用了1个信号量配额,直接跳出循环后没有对应协程执行<-sem释放配额,会导致后续逻辑异常
  • 布尔变量e存在可见性风险:对e的写入加了锁但读取没有加锁,多核场景下可能出现e已经被赋值为true但循环未及时感知的问题
  • 原代码末尾的信号量排空循环存在死锁风险:wgnested.Wait()已经保证所有子协程执行完成,所有信号量配额都已释放,额外的排空逻辑完全冗余,当信号量占满时还会触发死锁
优化方案

Go语言标准的并发停止信号方案是使用context.WithCancel,比布尔变量更安全、无竞态,也不会触发panic,修改后的代码如下:

package main

import (
	"context"
	"fmt"
	"math/rand"
	"strconv"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	var accounts int = 1
	var emails int = 1000

	for i := 0; i < accounts; i++ {
		wg.Add(1)

		go func(i int) {
			defer wg.Done()
			threads := 500 // 最大并发子协程数
			var orderMutex sync.Mutex
			var quantity = 2
			var orders = 0
			sem := make(chan bool, threads)
			var wgnested sync.WaitGroup
			// 创建可取消的context作为停止信号
			ctx, cancel := context.WithCancel(context.Background())
			defer cancel()

			for x := 0; x < emails; x++ {
				// 先检查是否已经触发停止,避免无意义的信号量占用
				select {
				case <-ctx.Done():
					break
				default:
				}

				sem <- true // 占用信号量

				orderMutex.Lock()
				if orders >= quantity {
					fmt.Println(strconv.Itoa(x) + " 达到数量上限,停止循环")
					orderMutex.Unlock()
					cancel() // 发送停止信号
					<-sem    // 释放当前占用的信号量,避免泄漏
					break
				}
				orderMutex.Unlock()

				wgnested.Add(1)
				go func() {
					defer wgnested.Done()
					defer func() { <-sem }()

					// 模拟下单逻辑
					if rand.Intn(50) == 7 {
						orderMutex.Lock()
						orders++
						orderMutex.Unlock()
					}

					fmt.Println(strconv.Itoa(i) + " 执行完成 ")
				}()
			}

			wgnested.Wait()
			fmt.Println(strconv.Itoa(i) + " 总下单数: " + strconv.Itoa(orders))
		}(i)
	}

	wg.Wait()
}
优化点说明
  • 用context作为停止信号,天然保证并发安全,不存在布尔变量的可见性问题
  • 触发停止条件时主动释放当前已占用的信号量,避免资源泄漏
  • 移除了冗余的信号量排空逻辑,依赖wgnested.Wait()保证所有子协程执行完成,消除死锁风险
  • 循环开头增加停止信号判断,避免达到停止条件后无意义的信号量占用和逻辑执行

内容的提问来源于stack exchange,提问作者Nick D

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 08:54:03