- 什么是竞争
- Mutex 的使用
什么是竞争
看下面的代码
package main
import (
"sync"
"fmt"
)
var total = 0
func add(num int,ws * sync.WaitGroup){
total += 1
ws.Done()
}
func main() {
var ws sync.WaitGroup
ws.Add(1000)
for i:=0;i<1000;i++{
go add(1,&ws)
}
ws.Wait()
fmt.Println(total)
}
我们对一个变量total 进行1000次 +1 操作,不过我们是在多个协程中进行的,猜猜结果如何,我们运行五次看结果
竞争发生的概率,和你的协程数量成正比,如果你的协程数很少的话,可能运行几百次发现不了这个现象
那如何避免这个问题呢?
Mutex
Mutex 用于提供一种加锁机制(Locking Mechanism),可确保在某时刻只有一个协程在临界区运行,以防止出现竞态条件。
直接看代码
package main
import (
"sync"
"fmt"
)
var total = 0
func add(num int,ws * sync.WaitGroup,mutex *sync.Mutex){
mutex.Lock()
total += 1
mutex.Unlock()
ws.Done()
}
func main() {
var ws sync.WaitGroup
mutex := sync.Mutex{}
ws.Add(1000)
for i:=0;i<1000;i++{
go add(1,&ws,&mutex)
}
ws.Wait()
fmt.Println(total)
}
运行结果如下
sync.Mutex 是一个结构体 在操作元素的时候 mutex.Lock()
加锁 执行完毕后 mutex.Unlock()
解锁
使用信道处理竞争
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 和信道,我们已经解决了竞态条件的问题。那么我们该选择使用哪一个?答案取决于你想要解决的问题。如果你想要解决的问题更适用于 Mutex,那么就用 Mutex。如果需要使用 Mutex,无须犹豫。而如果该问题更适用于信道,那就使用信道。:)
由于信道是 Go 语言很酷的特性,大多数 Go 新手处理每个并发问题时,使用的都是信道。这是不对的。Go 给了你选择 Mutex 和信道的余地,选择其中之一都可以是正确的。
总体说来,当 Go 协程需要与其他协程通信时,可以使用信道。而当只允许一个协程访问临界区时,可以使用 Mutex。
就我们上面解决的问题而言,我更倾向于使用 Mutex,因为该问题并不需要协程间的通信。所以 Mutex 是很自然的选择。
我的建议是去选择针对问题的工具,而别让问题去将就工具。:)
网友评论