为什么竞争检测器没有检测到这种竞争条件?

我目前正在学习 Go 编程语言,并且正在尝试原子包。

在此示例中,我生成了许多 Goroutine,它们都需要增加包级别变量。有多种方法可以避免竞争条件,但现在我想使用该atomic包来解决这个问题。

在我的 Windows PC ( ) 上运行以下代码时,go run main.go结果不是我期望的结果(我期望最终结果为 1000)。最终数字介于 900 到 1000 之间。在 Go Playground 中运行代码时,该值为 1000。

这是我正在使用的代码:https ://play.golang.org/p/8gW-AsKGzwq

var counter int64

var wg sync.WaitGroup


func main() {

    num := 1000

    wg.Add(num )

    for i := 0; i < num ; i++ {

        go func() {

            v := atomic.LoadInt64(&counter)

            v++

            atomic.StoreInt64(&counter, v)


            // atomic.AddInt64(&counter, 1)


            // fmt.Println(v)

            wg.Done()

        }()

    }

    wg.Wait()

    fmt.Println("final", counter)

}

go run main.go

final 931


go run main.go

final 960


go run main.go

final 918

我本以为竞争检测器会给出错误,但它没有:


go run -race main.go

final 1000

它输出正确的值(1000)。

我使用的是go版本go1.12.7 windows/amd64(目前最新版本)

我的问题:

  • 为什么竞争检测器没有给出错误,但是在没有竞争检测器的情况下运行代码时我是否看到不同的值?

  • 我的理论是,加载/存储组合不起作用的原因是这两个原子调用作为一个整体并不是原子的。在这种情况下我应该使用该atomic.AddInt64方法,对吗?

任何帮助将不胜感激 :)


慕码人8056858
浏览 101回答 1
1回答

一只甜甜圈

您的代码中没有任何恶意内容,因此这就是竞争检测器未检测到任何内容的原因。您的counter变量始终atomic通过启动的 goroutine 中的包进行访问,而不是直接访问。有时你得到 1000 个有时更少的原因是由于运行 goroutine 的活动线程的数量:GOMAXPROCS。在 Go Playground 上它是 1,所以任何时候你都有一个活动的 goroutine(所以基本上你的应用程序是按顺序执行的,没有任何并行性)。并且当前的 goroutine 调度器不会任意将 goroutine 停放。在你的本地机器上,你可能有一个多核 CPU,并且GOMAXPROCS默认为可用逻辑 CPU 的数量,因此GOMAXPROCS大于 1,因此你有多个并行运行的 goroutine (真正的并行,而不仅仅是并发)。看这个片段:v := atomic.LoadInt64(&counter)v++atomic.StoreInt64(&counter, v)您加载counter值并将其分配给v,您递增v,然后存储回递增的值v。如果 2 个并行 goroutine 同时执行此操作会发生什么?假设两者都加载 value 100。两者都会增加其本地副本:101. 两者都回写101,尽管应该是在102。是的,原子递增计数器的正确方法是atomic.AddInt64()这样使用:for i := 0; i < num; i++ {    go func() {        atomic.AddInt64(&counter, 1)        wg.Done()    }()}这样无论是什么,你总是会得到 1000 GOMAXPROCS。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go