Golang如何解决竞争条件
基本概念
临界段(Critical section),当一段程序并发运行时,部分共享资源不应该被多协程在同一时刻访问。这部分修改共享资源的代码被称为临界段。
竞争条件(Race condition),两个或多个进程彼此之间没有内在的制约关系,但是由于要抢占使用某个临界资源而产生制约关系。
案例分析
在多协程的运行环境下,代码counter++
存在竞争条件。
func counterWithoutLock() {
counter := 0
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
counter++
}()
}
wg.Wait()
println(counter) // counter<1000
}
对临界段counter++
加锁,sync.Mutex
是互斥锁,同一时刻,当且只有一个协程可以访问临界段,从而解决了竞争条件。
func counterWithLock() {
counter := 0
var wg sync.WaitGroup
var l sync.Mutex
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
l.Lock()
counter++
l.Unlock()
}()
}
wg.Wait()
println(counter) // counter=1000
}
golang的channel
也能实现和互斥锁一样的功能。有缓存channel,当通道的缓存已经占满,向通道写数据但无协程读,这个时候channel会堵塞。
func counterWithChannel() {
counter := 0
var wg sync.WaitGroup
ch := make(chan bool, 1)
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
ch <- true
counter++
<-ch
}()
}
wg.Wait()
println(counter) // counter=1000
}
后两段程序保证了临界段counter++
在同一时刻内,只有一个协程能对共享资源量counter
进行访问。