在goroutine

时间:2017-08-10 07:23:01

标签: go goroutine

我有一个去的程序如下。它启动NumberOfCPUs-1 goroutines并在每个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)
}

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

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)
}

x将是一些随机的大值。

我认为它可能与goroutine调度程序有关。在第一种情况下,goroutine的数量小于cpu核心的数量,因此main func可以与所有现有的goroutine一起执行。由于每个goroutine内部都没有进行系统调用,I / O或通道通信,因此goroutine调度程序将无法正常工作。并且因为goroutine没有被中断,更新的x没有机会被写回来。

在第二种情况下,goroutine的数量等于cpu核心的数量,为了让main func有机会运行,我在更新后time.Sleep(0)放置x。我想每次goroutine调度程序都会切换goroutine,更新的x将被写回原来的内存位置。

有人能证实我的想法吗?有没有错过?

感谢。

1 个答案:

答案 0 :(得分:4)

您有多个goroutine共享相同的变量x,并且具有不同步的读取和写入。你有数据竞赛。因此,x的结果未定义。使用选项-race运行竞赛检测器。请参阅Introducing the 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)
}

输出:

$ 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
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)
}

输出:

$ 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