gmutex.Mutex 互斥锁对象支持读写控制,互斥锁功能逻辑与标准库 sync.RWMutex 类似,可并发读但不可并发写。

互斥锁的设计细节,推荐阅读轻量级高清版的实现源码: https://github.com/gogf/gf/blob/master/os/gmutex/gmutex.go

使用方式

  1. import "github.com/gogf/gf/v2/os/gmutex"

接口文档

https://pkg.go.dev/github.com/gogf/gf/v2/os/gmutex

  1. type Mutex
  2. func (m *Mutex) LockFunc(f func())
  3. func (m *Mutex) TryLockFunc(f func()) (result bool)
  4. type RWMutex
  5. func New() *RWMutex
  6. func (m *RWMutex) LockFunc(f func())
  7. func (m *RWMutex) RLockFunc(f func())
  8. func (m *RWMutex) TryLockFunc(f func()) (result bool)
  9. func (m *RWMutex) TryRLockFunc(f func()) (result bool)
  1. 该互斥锁模块最大的特点是支持 Try* 方法以及 *Func 方法。
  2. Try* 方法用于实现尝试获得特定类型的锁,如果获得锁成功则立即返回 true,否则立即返回 false,不会阻塞等待,这对于需要使用非阻塞锁机制的业务逻辑非常实用。
  3. *Func 方法使用闭包匿名函数的方式实现特定作用域的并发安全锁控制,这对于特定代码块的并发安全控制特别方便,由于内部使用了 defer 来释放锁,因此即使函数内部产生异常错误,也不会影响锁机制的安全性控制。

基准测试

gmutex.Mutex 与标准库的 sync.Mutexsync.RWMutex 的基准测试对比结果: gmutex_bench_test.go

  1. goos: linux
  2. goarch: amd64
  3. pkg: github.com/gogf/gf/v2/os/gmutex
  4. Benchmark_Mutex_LockUnlock-4 50000000 31.5 ns/op
  5. Benchmark_RWMutex_LockUnlock-4 30000000 54.1 ns/op
  6. Benchmark_RWMutex_RLockRUnlock-4 50000000 27.9 ns/op
  7. Benchmark_GMutex_LockUnlock-4 50000000 27.2 ns/op
  8. Benchmark_GMutex_TryLock-4 100000000 16.7 ns/op
  9. Benchmark_GMutex_RLockRUnlock-4 50000000 38.0 ns/op
  10. Benchmark_GMutex_TryRLock-4 100000000 16.8 ns/op

示例1,基本使用

  1. package main
  2. import (
  3. "time"
  4. "github.com/gogf/gf/v2/os/gctx"
  5. "github.com/gogf/gf/v2/os/glog"
  6. "github.com/gogf/gf/v2/os/gmutex"
  7. )
  8. func main() {
  9. ctx := gctx.New()
  10. mu := gmutex.New()
  11. for i := 0; i < 10; i++ {
  12. go func(n int) {
  13. mu.Lock()
  14. defer mu.Unlock()
  15. glog.Print(ctx, "Lock:", n)
  16. time.Sleep(time.Second)
  17. }(i)
  18. }
  19. for i := 0; i < 10; i++ {
  20. go func(n int) {
  21. mu.RLock()
  22. defer mu.RUnlock()
  23. glog.Print(ctx, "RLock:", n)
  24. time.Sleep(time.Second)
  25. }(i)
  26. }
  27. time.Sleep(15 * time.Second)
  28. }

执行后,终端输出:

  1. 2019-07-13 16:19:55.417 Lock: 0
  2. 2019-07-13 16:19:56.421 Lock: 1
  3. 2019-07-13 16:19:57.424 RLock: 0
  4. 2019-07-13 16:19:57.424 RLock: 4
  5. 2019-07-13 16:19:57.425 RLock: 8
  6. 2019-07-13 16:19:57.425 RLock: 2
  7. 2019-07-13 16:19:57.425 RLock: 7
  8. 2019-07-13 16:19:57.425 RLock: 5
  9. 2019-07-13 16:19:57.425 RLock: 9
  10. 2019-07-13 16:19:57.425 RLock: 1
  11. 2019-07-13 16:19:57.425 RLock: 6
  12. 2019-07-13 16:19:57.425 RLock: 3
  13. 2019-07-13 16:19:58.429 Lock: 3
  14. 2019-07-13 16:19:59.433 Lock: 4
  15. 2019-07-13 16:20:00.438 Lock: 5
  16. 2019-07-13 16:20:01.443 Lock: 6
  17. 2019-07-13 16:20:02.448 Lock: 7
  18. 2019-07-13 16:20:03.452 Lock: 8
  19. 2019-07-13 16:20:04.456 Lock: 9
  20. 2019-07-13 16:20:05.461 Lock: 2

这里使用 glog 打印的目的,是可以方便地看到打印输出的时间。可以看到,在第3秒的时候,读锁抢占到了机会,由于 gmutex.Mutex 对象支持并发读但不支持并发写,因此读锁抢占后迅速执行完毕;而写锁依旧保持每秒打印一条日志继续执行。

示例2, *Func 使用

  1. package main
  2. import (
  3. "time"
  4. "github.com/gogf/gf/v2/os/glog"
  5. "github.com/gogf/gf/v2/os/gmutex"
  6. )
  7. func main() {
  8. mu := gmutex.New()
  9. go mu.LockFunc(func() {
  10. glog.Println("lock func1")
  11. time.Sleep(1 * time.Second)
  12. })
  13. time.Sleep(time.Millisecond)
  14. go mu.LockFunc(func() {
  15. glog.Println("lock func2")
  16. })
  17. time.Sleep(2 * time.Second)
  18. }

执行后,终端输出:

  1. 2019-07-13 16:28:10.381 lock func1
  2. 2019-07-13 16:28:11.385 lock func2

可以看到,使用 *Func 方法实现特定作用域的锁控制非常方便。