过 Mutex 和信道来处理竞态条件
多个协程访问同一个变量导致竞态条件,都在改变变量的值
如果在任意时刻只允许一个 Go 协程访问临界区,那么就可以避免竞态条件。而使用 Mutex 也可以避免竞态条件
Mutex
Mutex 用于提供一种加锁机制(Locking Mechanism),可确保在某时刻只有一个协程在临界区运行,以防止出现竞态条件
Mutex 在 sync
包内。Mutex 定义了两个方法:Lock
和 Unlock
。所有在 Lock 和 Unlock 之间的代码,都只能由一个 Go 协程执行,于是就可以避免竞态条件。
mutex.Lock()
x = x + 1
mutex.Unlock()
package main
import (
"fmt"
"sync"
)
var x = 0
func increment(wg *sync.WaitGroup,m *sync.Mutex) {
//m.Lock()
x = x + 1
//m.Unlock()
wg.Done()
}
func main() {
var w sync.WaitGroup
var m sync.Mutex
for i := 0; i < 1000; i++ {
w.Add(1)
go increment(&w,&m)
}
w.Wait()
fmt.Println("final value of x", x)
}
结果: 每次运行结果都不是1000
final value of x 972
使用Mutex
final value of x 1000
使用信道处理竞态条件
由于缓冲信道的容量为 1,已写入数据后,再试图写入该信道数据时,都会发生阻塞,直到信道的值被读取后,信道的值才能被再次写入。也就是只允许一个协程操作临界值
package main
import (
"fmt"
"sync"
)
var x = 0
func increment(wg *sync.WaitGroup, ch chan bool) {
ch <- true
x = x + 1
<- ch
wg.Done()
}
func main() {
var w sync.WaitGroup
ch := make(chan bool, 1)
for i := 0; i < 1000; i++ {
w.Add(1)
go increment(&w, ch)
}
w.Wait()
fmt.Println("final value of x", x)
}
Mutex vs 信道
当 Go 协程需要与其他协程通信时,可以使用信道。而当只允许一个协程访问临界区时,可以使用 Mutex
我的建议是去选择针对问题的工具,而别让问题去将就工具。😃