Go 可重入锁如何实现?

参考解析

概念:

可重入锁又称为递归锁,是指在同一个线程在外层方法获取锁的时候,在进入该线程的内层方法时会自动获取锁,不会因为之前已经获取过还没释放再次加锁导致死锁

为什么Go语言中没有可重入锁?

Mutex 不是可重入的锁。Mutex 的实现中没有记录哪个 goroutine 拥有这把锁。理论上,任何 goroutine 都可以随意地 Unlock 这把锁,所以没办法计算重入条件,并且Mutex 重复Lock会导致死锁。

如何实现可重入锁?

实现一个可重入锁需要这两点:

- 记住持有锁的线程
- 统计重入的次数

  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. "runtime"
  6. "strconv"
  7. "sync"
  8. "sync/atomic"
  9. )
  10. type ReentrantLock struct {
  11. sync.Mutex
  12. recursion int32 // 这个goroutine 重入的次数
  13. owner int64 // 当前持有锁的goroutine id
  14. }
  15. // Get returns the id of the current goroutine.
  16. func GetGoroutineID() int64 {
  17. var buf [64]byte
  18. var s = buf[:runtime.Stack(buf[:], false)]
  19. s = s[len("goroutine "):]
  20. s = s[:bytes.IndexByte(s, )]
  21. gid, _ := strconv.ParseInt(string(s), 10, 64)
  22. return gid
  23. }
  24. func NewReentrantLock() sync.Locker {
  25. res := &ReentrantLock{
  26. Mutex: sync.Mutex{},
  27. recursion: 0,
  28. owner: 0,
  29. }
  30. return res
  31. }
  32. // ReentrantMutex 包装一个Mutex,实现可重入
  33. type ReentrantMutex struct {
  34. sync.Mutex
  35. owner int64 // 当前持有锁的goroutine id
  36. recursion int32 // 这个goroutine 重入的次数
  37. }
  38. func (m *ReentrantMutex) Lock() {
  39. gid := GetGoroutineID()
  40. // 如果当前持有锁的goroutine就是这次调用的goroutine,说明是重入
  41. if atomic.LoadInt64(&m.owner) == gid {
  42. m.recursion++
  43. return
  44. }
  45. m.Mutex.Lock()
  46. // 获得锁的goroutine第一次调用,记录下它的goroutine id,调用次数加1
  47. atomic.StoreInt64(&m.owner, gid)
  48. m.recursion = 1
  49. }
  50. func (m *ReentrantMutex) Unlock() {
  51. gid := GetGoroutineID()
  52. // 非持有锁的goroutine尝试释放锁,错误的使用
  53. if atomic.LoadInt64(&m.owner) != gid {
  54. panic(fmt.Sprintf("wrong the owner(%d): %d!", m.owner, gid))
  55. }
  56. // 调用次数减1
  57. m.recursion--
  58. if m.recursion != 0 { // 如果这个goroutine还没有完全释放,则直接返回
  59. return
  60. }
  61. // 此goroutine最后一次调用,需要释放锁
  62. atomic.StoreInt64(&m.owner, -1)
  63. m.Mutex.Unlock()
  64. }
  65. func main() {
  66. var mutex = &ReentrantMutex{}
  67. mutex.Lock()
  68. mutex.Lock()
  69. fmt.Println(111)
  70. mutex.Unlock()
  71. mutex.Unlock()
  72. }