Golang 并发写入变量 - 为什么这段代码有效?

Golang concurrency write to variable - why this code works?

我正在学习 Golang 中与并发相关的问题。我写了一些代码:

package main

import (
    "fmt"
    "time"
)

func incr(num *int) {
    *num = *num + 1

}

func main() {
    var a = 0

    for i := 0; i < 50; i++ {
        go incr(&a)
    }

    incr(&a)

    time.Sleep(1 * time.Second)
    fmt.Println(a)
}

这段代码的结果是:51

在这段代码中,我声明了 a 变量,我将在 50 运行 goroutines 中增加它。根据我的阅读和理解,这段代码应该会失败,因为多个 goroutine 正在写入相同的内存地址。在这种情况下,我应该添加 sync.Mutex 锁来解决这个问题。

代码在 playground 中可用:https://play.golang.org/p/Tba9pfpxaHY

你能解释一下这个程序到底发生了什么吗?

你猜怎么着?我 运行 你的应用程序,我得到不同的输出:有时 49, 有时 48,有时 50 (有时 51)。

如果您 运行 您的应用启用了竞争检测器 (go run -race play.go),它会告诉您存在数据竞争:

==================
WARNING: DATA RACE
Read at 0x00c00009a010 by goroutine 7:
  main.incr()
      /home/icza/gows/src/play/play.go:9 +0x3a

Previous write at 0x00c00009a010 by goroutine 6:
  main.incr()
      /home/icza/gows/src/play/play.go:9 +0x50

Goroutine 7 (running) created at:
  main.main()
      /home/icza/gows/src/play/play.go:17 +0x83

Goroutine 6 (finished) created at:
  main.main()
      /home/icza/gows/src/play/play.go:17 +0x83
==================

当您有数据竞争时,您的应用程序的行为是未定义。 "Seemingly working sometimes" 也符合 "undefined" 行为,但未定义也意味着它也可以做任何其他事情。

查看相关问题:

Is it safe to read a function pointer concurrently without a lock?