欢迎关注微信公众号:全栈工厂
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的读写添加了互斥锁,这样我们再执行的时候就不再报竟态条件警告了。