基本定义
Mutex 实现了 Locker 接口:
1 2 3 4
| type Locker interface { Lock() Unlock() }
|
简单来说,互斥锁 Mutex 就提供两个方法 Lock 和 Unlock:进入临界区之前调用 Lock 方法,退出临界区的时候调用 Unlock 方法:
1 2
| func(m *Mutex)Lock() func(m *Mutex)Unlock()
|
不使用锁的例子,创建10个goroutine,每个goroutine执行10万次的加1操作,最后期望的结果是一百万。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import ( "fmt" "sync" ) func main() { var count = 0 var wg sync.WaitGroup wg.Add(10) for i := 0; i < 10; i++ { go func() { defer wg.Done() for j := 0; j < 100000; j++ { count++ } }() } wg.Wait() fmt.Println(count) }
|
由于 count++ 不是一个原子操作,且没有上锁,所以得出的结果值肯定小于一百万。
race detector
https://go.dev/blog/race-detector
在编译(compile)、测试(test)或者运行(run)Go 代码的时候,加上 -race 参数,就有可能发现并发问题。
-race 只能通过真正对实际地址进行读写访问的时候才能探测,所以它并不能在编译的时候发现 data race 的问题。而且只有在触发了 data race 之后,才能检测到,如果碰巧没有触发,则检测不到。-race 影响性能,不推荐在生产环境使用。
github 上还存在其他的 race detector 工具,例如: https://github.com/amit-davidson/Chronos
Mutex 用法
上面例子使用 Mutex 后如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| package main
import ( "fmt" "sync" )
func main() { var mu sync.Mutex var count = 0 var wg sync.WaitGroup wg.Add(10)
for i := 0; i < 10; i++ { go func() { defer wg.Done() for j := 0; j < 100000; j++ { mu.Lock() count++ mu.Unlock() } }() } wg.Wait() fmt.Println(count) }
|
或者采用嵌入字段的方式,将 Mutex 嵌入到 struct 中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| func main() { var counter Counter var wg sync.WaitGroup wg.Add(10) for i := 0; i < 10; i++ { go func() { defer wg.Done() for j := 0; j < 100000; j++ { counter.Lock() counter.Count++ counter.Unlock() } }() } wg.Wait() fmt.Println(counter.Count) }
type Counter struct { sync.Mutex Count uint64 }
|
或者把获取锁、释放锁、计数加一的逻辑封装成一个方法,对外不需要暴露锁等逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| func main() { var counter Counter
var wg sync.WaitGroup wg.Add(10)
for i := 0; i < 10; i++ { go func() { defer wg.Done() for j := 0; j < 100000; j++ { counter.Incr() } }() } wg.Wait() fmt.Println(counter.Count()) }
type Counter struct { CounterType int Name string
mu sync.Mutex count uint64 }
func (c *Counter) Incr() { c.mu.Lock() c.count++ c.mu.Unlock() }
func (c *Counter) Count() uint64 { c.mu.Lock() defer c.mu.Unlock() return c.count }
|