- 什么是竞争
- 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 操作,不过我们是在多个协程中进行的,猜猜结果如何,我们运行五次看结果
![1594482-1d6d52b5f66fbdbc](https://i-blog.csdnimg.cn/blog_migrate/9c0e10af0e14708a25d89a46e0aed5c0.webp?x-image-process=image/format,png)
image.png
![1594482-f2c8b65b07c4b2a7](https://i-blog.csdnimg.cn/blog_migrate/146b167ff935a608ebf06138e59eff6c.webp?x-image-process=image/format,png)
image.png
![1594482-11fbd76e66019e86](https://i-blog.csdnimg.cn/blog_migrate/c484bc5dedfde0aa91e924984e4ad1f7.webp?x-image-process=image/format,png)
image.png
![1594482-ff35de8071d6f5ea](https://i-blog.csdnimg.cn/blog_migrate/2215337110d501a2d87d8a88bf927e3e.webp?x-image-process=image/format,png)
image.png
![1594482-341c0f7c2ac13548](https://i-blog.csdnimg.cn/blog_migrate/29a785451481a68d3940e03c498c3afb.webp?x-image-process=image/format,png)
image.png
竞争发生的概率,和你的协程数量成正比,如果你的协程数很少的话,可能运行几百次发现不了这个现象
那如何避免这个问题呢?
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)
}
运行结果如下
![1594482-1502ebb48c036990](https://i-blog.csdnimg.cn/blog_migrate/1e0c18e19b4362f88f71ce2517d13eb0.webp?x-image-process=image/format,png)
image.png
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 是很自然的选择。
我的建议是去选择针对问题的工具,而别让问题去将就工具。:)