【Golang】竞态条件 (Race Conditions)

欢迎关注微信公众号:全栈工厂

1. 什么是竞态条件?

竞态条件是指在并发环境中,当有多个事件同时访问同一个临界资源时,由于多个事件的并发执行顺序的不确定,从而导致程序输出结果的不确定,这种情况我们称之为竞态条件 (Race Conditions)或者竞争冒险(race hazard)。
在golang的多协程环境中比较容易出现竞态条件,例如以下代码:

package main

import (
    "sync"
)

var wg sync.WaitGroup
var Total = 0

func main() {
    for concurrence := 0; concurrence < 1000; concurrence++ {
        wg.Add(1)
        go goroutine()
    }
    wg.Wait()
    println("Total:", Total)
}

func goroutine() {
    Total++
    wg.Done()
}

上述代码,我们执行了5次,没吃执行结果都不一样:


其最主要的原因就是由于多协程同时访问临界变量Total,从而出现脏读,导致1000个协程的累加和最终小于1000

2. 怎么发现竞态条件?

在golang 1.1版本中,引入了竞态条件检测工具(race detector),只要带编译执行时加入 -race 参数即可:

go build -race main.go
或:
go run -race main.go

用上述命令执行程序,我们会发现:


执行结果警告第20行出现竟态条件,这样我们就能快速定位问题代码。

3. 如何修复竟态条件?

给临界代码去添加互斥锁可以很好的解决竞态条件问题,例如:

package main

import (
    "sync"
)

var wg  sync.WaitGroup
var Total = 0
var mu sync.Mutex

func main() {
    for concurrence := 0; concurrence < 1000; concurrence++ {
        wg.Add(1)
        go goroutine(&wg)
    }
    wg.Wait()
    println("Total:", Total)
}

func goroutine(wg *sync.WaitGroup) {
    mu.Lock()
    Total++
    mu.Unlock()
    wg.Done()
}

我们对全局变量Total的读写添加了互斥锁,这样我们再执行的时候就不再报竟态条件警告了。

你可能感兴趣的:(【Golang】竞态条件 (Race Conditions))