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

Go使用goroutine并发取compute最大值context取消时返回0或死锁如何解决

问题排查与解决方案

问题根源

返回0和偶现死锁的问题主要由以下几个原因导致:

  • 主协程未等待子goroutine执行:原代码中findMax函数启动指定数量的goroutine后立刻返回,此时子goroutine还在执行compute的休眠逻辑,max变量还没被赋值,所以始终返回0。
  • 并发读写无同步导致数据竞争:多个goroutine同时修改max、num变量,没有加锁保护,会出现读写冲突,即使等待goroutine执行完结果也不准确,部分同步逻辑不当的实现还会触发死锁。
  • 未实现反复调用compute的逻辑:需求要求每个goroutine在ctx取消前反复调用compute,原代码每个goroutine只调用一次compute就退出,不符合功能要求。
  • 闭包变量捕获错误:匿名goroutine直接捕获外部的num变量,多个goroutine会同时修改同一个num,值会被随机覆盖。

修复后代码

package main

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

func findMax(ctx context.Context, concurrency int) uint64 {
	var (
		max uint64 = 0
		mu  sync.Mutex
		wg  sync.WaitGroup
	)

	// 注册要等待的goroutine数量
	wg.Add(concurrency)
	for i := 0; i < concurrency; i++ {
		go func() {
			defer wg.Done()
			// 循环调用compute直到ctx被取消
			for {
				select {
				case <-ctx.Done():
					return
				default:
					num := compute()
					// 加锁保护max变量的读写,避免数据竞争
					mu.Lock()
					if num > max {
						max = num
					}
					mu.Unlock()
				}
			}
		}()
	}

	// 等待所有goroutine全部退出后再返回结果
	wg.Wait()
	return max
}

func compute() uint64 {
	// NOTE: This is a MOCK implementation of the blocking operation.
	time.Sleep(time.Duration(rand.Int63n(100)) * time.Millisecond)
	return rand.Uint64()
}

func main() {
	maxDuration := 2 * time.Second
	concurrency := 10

	ctx, cancel := context.WithTimeout(context.Background(), maxDuration)
	defer cancel()

	max := findMax(ctx, concurrency)
	fmt.Println(max)
}

改动说明

  • 新增sync.WaitGroup:等待所有启动的goroutine在ctx取消后全部退出,避免findMax提前返回。
  • 新增sync.Mutex:保护max变量的并发读写,避免数据竞争导致的结果错误。
  • 每个goroutine内部增加循环逻辑:只要ctx未取消就持续调用compute,符合反复调用的需求。
  • 修复闭包变量捕获:num变量改为goroutine内部的局部变量,避免多个goroutine同时修改同一个外部num导致值覆盖。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.16 06:54:03