首页 > 解决方案 > 引擎盖下发生了什么,所以地图的这种并发使用是活泼的

问题描述

在下面的示例中,竞态检测器将触发错误。不过,我对此很好,因为它不会更改键、地图标题(如果我可以说的话),我很难弄清楚比赛的原因是什么。我根本不明白引擎盖下发生了什么,因此发出了种族检测。

package main

import (
    "fmt"
    "sync"
)

// scores holds values incremented by multiple goroutines.
var scores = make(map[string]int)

func main() {
    var wg sync.WaitGroup
    wg.Add(2)
    scores["A"] = 0
    scores["B"] = 0
    go func() {
        for i := 0; i < 1000; i++ {
            // if _, ok := scores["A"]; !ok {
            //  scores["A"] = 1
            // } else {
            scores["A"]++
            // }
        }
        wg.Done()
    }()

    go func() {
        for i := 0; i < 1000; i++ {
            scores["B"]++ // Line 28
        }
        wg.Done()
    }()

    wg.Wait()
    fmt.Println("Final scores:", scores)
}

标签: go

解决方案


映射值是不可寻址的,因此递增整数值需要将它们写回映射本身。

线

scores["A"]++

相当于

tmp := scores["A"]
scores["A"] = tmp + 1

如果您使用指针使整数值可寻址,并在分派 goroutine 之前分配所有键,您可以看到地图本身不再存在竞争:

var scores = make(map[string]*int)

func main() {
    var wg sync.WaitGroup
    wg.Add(2)
    scores["A"] = new(int)
    scores["B"] = new(int)
    go func() {
        for i := 0; i < 1000; i++ {
            (*scores["A"])++
        }
        wg.Done()
    }()

    go func() {
        for i := 0; i < 1000; i++ {
            (*scores["B"])++
        }
        wg.Done()
    }()

    wg.Wait()
    fmt.Println("Final scores:", scores)
}

推荐阅读