当在循环内开启 goroutine 时,一定要非常小心地处理循环变量,避免它们在 goroutine 之间发生竞争,这是常见的 Go 并发问题之一。在当前给出的代码中,开启 goroutine 时使用的 tmpChain 变量就是循环变量 chain 的一个副本,但是它的值只会在循环开始时被赋值,而不会在每次迭代时重新赋值。因此,在你的代码里,如果开启了一些长时间运行的 goroutine 并且在迭代 chainCurrencyAddrMap 之后执行了它们,它们将会看到那个过时的 tmpChain 变量,而不是它们应该看到的当前值。这将导致 goroutine 处理的信息混乱,导致错误。

为了修复这个问题,一种解决方法是将循环变量传递给 goroutine 的参数中,这将确保在 goroutine 中使用的始终是变量的当前值。可以通过将 tmpChain := chain 改为 使用函数参数传递来实现。修改后的代码如下:

func getAddrDistInfoPatch(c * gin.Context,

rawData *graph_raw.GraphRawData, 
chainCurrencyAddrMap map[string]map[string][]string,
column []string) (inFlowMap map[string]map[string]map[string]aModel.AddressInfo, err error) {

mu := &sync.Mutex{}
wg := &sync.WaitGroup{}

inFlowMap = make(map[string]map[string]map[string]aModel.AddressInfo)

for chain, currencyInfo := range chainCurrencyAddrMap {
    for currency, addrs := range currencyInfo {
        limit := make(chan bool, 50)
        for i := 0; i < len(addrs); i += 50 {
            start := i
            wg.Add(1)
            limit <- true
            go func(c string, cr string) {
                defer wg.Done()
                group := collections.SliceSubRange(addrs, start, 50)
                req := aModel.AddressInfoReq{
                    Direction: contract.LeftDirection,
                    Chain:     c,
                    Currency:  cr,
                    MinAmount: rawData.SessionInfo.MinAmount,
                    Column:    column,
                    Addr:      group,
                    BeginTime: rawData.SessionInfo.BeginTime,
                    EndTime:   rawData.SessionInfo.EndTime,
                }

                tmp := map[string]aModel.AddressInfo{}
                resp := safeis.Call(c, "address", "getDistCurrencyAddrInfo",
                    http.MethodPost, nil, req, nil)
                if resp.Code != serror.Success {
                    slog.Errorf(c, "call address/getDistCurrencyAddrInfo failed, resp:%+v", resp)
                    return
                }
                _ = resp.ToStruct(&tmp)
                <-limit

                mu.Lock()
                defer mu.Unlock()

                for k, v := range tmp {
                    if _, ok := inFlowMap[k]; !ok {
                        inFlowMap[k] = make(map[string]map[string]aModel.AddressInfo)
                    }
                    if _, ok := inFlowMap[k][chain]; !ok {
                        inFlowMap[k][chain] = make(map[string]aModel.AddressInfo)
                    }
                    inFlowMap[k][chain][currency] = v
                }
            }(chain, currency)
        }
    }
}

wg.Wait()
return inFlowMap, nil

}
在上述代码中,通过将 tmpChain 赋值替换为将 chain 和 currency 作为参数传递给匿名函数,解决了迭代过程中 goroutine 错误描述的问题。

标签: none

添加新评论