goroutine中更新全局变量的不同行为

wyh*_*o31 -1 go goroutine

我有一个如下所示的程序。它启动NumberOfCPUs-1goroutines 并且在每个 goroutine 中只更新全局变量x。输出是x = 0

func main() {
    var x int
    threads := runtime.GOMAXPROCS(0)-1
    for i := 0; i < threads; i++ {
        go func() {
            for {
                x++
            }
        }()
    }
    time.Sleep(time.Second)
    fmt.Println("x =", x)
}
Run Code Online (Sandbox Code Playgroud)

如果我稍微改变一下程序,就像这样:

func main() {
    var x int
    threads := runtime.GOMAXPROCS(0)
    for i := 0; i < threads; i++ {
        go func() {
            for {
                x++
                time.Sleep(0)
            }
        }()
    }
    time.Sleep(time.Second)
    fmt.Println("x =", x)
}
Run Code Online (Sandbox Code Playgroud)

x 将是一些随机的大值。

我认为这可能与 goroutine 调度程序有关。在第一种情况下,goroutines 的数量小于 cpu 内核的数量,因此mainfunc 可以与所有现有的 goroutines 一起执行。因为在每个 goroutine 内部都没有发生系统调用、I/O 或通道通信,所以 goroutine 调度程序将无法工作。并且因为 goroutines 没有被中断,updatedx没有机会被写回。

而在第二种情况下,goroutines 的数量等于 cpu 内核的数量,为了让mainfunc 有机会运行,我放了一个time.Sleep(0)after update x。我认为每次 goroutine 调度器切换 goroutines 时,updatedx都会写回其原始内存位置。

有人能证实我的想法吗?有什么遗漏吗?

谢谢。

pet*_*rSO 5

你有多个 goroutine 共享同一个变量x,读写不同步。你有数据竞争。因此,您的结果x未定义。使用选项-race来运行竞争检测器。请参阅Go Race Detector 简介


package main

import (
    "fmt"
    "runtime"
    "time"
)

func main() {
    var x int
    threads := runtime.GOMAXPROCS(0) - 1
    for i := 0; i < threads; i++ {
        go func() {
            for {
                x++
            }
        }()
    }
    time.Sleep(time.Second)
    fmt.Println("x =", x)
}
Run Code Online (Sandbox Code Playgroud)

输出:

$ go run -race race1.go
==================
WARNING: DATA RACE
Read at 0x00c420084010 by goroutine 7:
  main.main.func1()
      /home/peter/gopath/src/race1.go:15 +0x3b

Previous write at 0x00c420084010 by goroutine 6:
  main.main.func1()
      /home/peter/gopath/src/race1.go:15 +0x54

Goroutine 7 (running) created at:
  main.main()
      /home/peter/gopath/src/race1.go:13 +0xb6

Goroutine 6 (running) created at:
  main.main()
      /home/peter/gopath/src/race1.go:13 +0xb6
==================
x = 24717968
Found 1 data race(s)
exit status 66
Run Code Online (Sandbox Code Playgroud)
package main

import (
    "fmt"
    "runtime"
    "time"
)

func main() {
    var x int
    threads := runtime.GOMAXPROCS(0)
    for i := 0; i < threads; i++ {
        go func() {
            for {
                x++
                time.Sleep(0)
            }
        }()
    }
    time.Sleep(time.Second)
    fmt.Println("x =", x)
}
Run Code Online (Sandbox Code Playgroud)

输出:

$ go run -race race2.go
==================
WARNING: DATA RACE
Read at 0x00c4200140d0 by goroutine 7:
  main.main.func1()
      /home/peter/gopath/src/race2.go:15 +0x3b

Previous write at 0x00c4200140d0 by goroutine 6:
  main.main.func1()
      /home/peter/gopath/src/race2.go:15 +0x54

Goroutine 7 (running) created at:
  main.main()
      /home/peter/gopath/src/race2.go:13 +0xb3

Goroutine 6 (running) created at:
  main.main()
      /home/peter/gopath/src/race2.go:13 +0xb3
==================
==================
WARNING: DATA RACE
Read at 0x00c4200140d0 by goroutine 8:
  main.main.func1()
      /home/peter/gopath/src/race2.go:15 +0x3b

Previous write at 0x00c4200140d0 by goroutine 6:
  main.main.func1()
      /home/peter/gopath/src/race2.go:15 +0x54

Goroutine 8 (running) created at:
  main.main()
      /home/peter/gopath/src/race2.go:13 +0xb3

Goroutine 6 (running) created at:
  main.main()
      /home/peter/gopath/src/race2.go:13 +0xb3
==================
x = 14739962
Found 2 data race(s)
exit status 66
Run Code Online (Sandbox Code Playgroud)