Golang – 为什么会发生这种竞态条件?

huangapple go评论77阅读模式
英文:

Golang - Why does this race condition occur?

问题

这个程序中的竞争条件是由于对全局变量 glo 的并发访问引起的。在 main 函数中,有一个无限循环,不断向 quit 通道发送数据,并且在每次发送后将 glo 的值加一。同时,在 test 函数中,会打印出 glo 的值。

n 的值较小时(如 n := 10000),两个 goroutine 的执行速度相对较慢,因此在 test 函数中打印出的 glo 的值总是小于等于 n

但是,当 n 的值较大时(如 n := 1000000),两个 goroutine 的执行速度变得更快,它们会更频繁地竞争对 glo 的访问。这就导致了竞争条件,使得 glo 的值可能会超过 n

要解决这个竞争条件,你可以使用互斥锁(sync.Mutex)来保护对 glo 的并发访问。在每次对 glo 的访问之前,先获取锁,然后释放锁。这样可以确保每次只有一个 goroutine 能够访问 glo,避免竞争条件的发生。

英文:
package main
import "fmt"

var quit chan int
var glo int

func test() {
	fmt.Println(glo)
}

func main() {
	glo = 0
    n := 10000
	quit = make(chan int, n)
	go test()
	for {
		quit <- 1
		glo++
	}
}

Situation:

The above program outputs 10000. But when I assign a bigger number to n (e.g. n := 1000000), the output will be a random number less than n.

I haven't called runtime.GOMAXPROCS(), so these two goroutines can't run in parallel. Executing go run -race to detect race conditions, ends up without any warnings.

Question:

Why does this race condition occur?

答案1

得分: 2

由于maintest goroutine之间没有同步,你无法确定test中的fmt.Println调用会在什么时候发生。

当使用GOMAXPROCS = 1运行时,答案实际上取决于调度器何时决定停止执行main并切换到test。循环中的发送操作是调度器可以切换到另一个goroutine的一个点,因此通过足够的循环迭代,你可以期望test在某个时刻有机会执行。它不一定会在每次运行时的相同迭代中切换,这导致结果的变化。

至于使用竞争检测器来捕获此问题,它对我来说成功地捕获了问题:

$ go run -race test.go
==================
WARNING: DATA RACE
Read by goroutine 5:
  main.test()
      /../test.go:8 +0x6e

Previous write by main goroutine:
  main.main()
      /.../test.go:18 +0xfe

Goroutine 5 (running) created at:
  main.main()
      /.../test.go:15 +0x8f
==================
...
英文:

As there is no synchronisation between the main and test goroutines, you don't know at what point the fmt.Println call in test will happen.

When running with GOMAXPROCS = 1, the answer will essentially depend on when the scheduler decides to stop executing main and switch to test. The send operation within the loop is one point where the scheduler can switch over to another goroutine, so with enough iterations of the loop you'd expect test to get a chance to execute at some point. It isn't necessarily going to switch at the same iteration every run leading to the variation in results.

As for catching this with the race detector, it successfully catches the problem for me:

$ go run -race test.go
==================
WARNING: DATA RACE
Read by goroutine 5:
  main.test()
      /../test.go:8 +0x6e

Previous write by main goroutine:
  main.main()
      /.../test.go:18 +0xfe

Goroutine 5 (running) created at:
  main.main()
      /.../test.go:15 +0x8f
==================
...

huangapple
  • 本文由 发表于 2015年5月9日 14:39:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/30136884.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定