面试官:Go 如何实现可重入锁

大家好,我是木川

一、什么是可重入锁

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

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

二、如何实现可重入锁

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

  • 记住持有锁的线程

  • 统计重入的次数

package main

import (
 "bytes"
 "fmt"
 "runtime"
 "strconv"
 "sync"
 "sync/atomic"
)

type ReentrantLock struct {
 sync.Mutex
 recursion int32 // 这个goroutine 重入的次数
 owner     int64 // 当前持有锁的goroutine id
}

// Get returns the id of the current goroutine.
func GetGoroutineID() int64 {
 var buf [64]byte
 var s = buf[:runtime.Stack(buf[:], false)]
 s = s[len("goroutine "):]
 s = s[:bytes.IndexByte(s, ' ')]
 gid, _ := strconv.ParseInt(string(s), 10, 64)
 return gid
}

func NewReentrantLock() sync.Locker {
 res := &ReentrantLock{
  Mutex:     sync.Mutex{},
  recursion: 0,
  owner:     0,
 }
 return res
}

// ReentrantMutex 包装一个Mutex,实现可重入
type ReentrantMutex struct {
 sync.Mutex
 owner     int64 // 当前持有锁的goroutine id
 recursion int32 // 这个goroutine 重入的次数
}

func (m *ReentrantMutex) Lock() {
 gid := GetGoroutineID()
 // 如果当前持有锁的goroutine就是这次调用的goroutine,说明是重入
 if atomic.LoadInt64(&m.owner) == gid {
  m.recursion++
  return
 }
 m.Mutex.Lock()
 // 获得锁的goroutine第一次调用,记录下它的goroutine id,调用次数加1
 atomic.StoreInt64(&m.owner, gid)
 m.recursion = 1
}

func (m *ReentrantMutex) Unlock() {
 gid := GetGoroutineID()
 // 非持有锁的goroutine尝试释放锁,错误的使用
 if atomic.LoadInt64(&m.owner) != gid {
  panic(fmt.Sprintf("wrong the owner(%d): %d!", m.owner, gid))
 }
 // 调用次数减1
 m.recursion--
 if m.recursion != 0 { // 如果这个goroutine还没有完全释放,则直接返回
  return
 }
 // 此goroutine最后一次调用,需要释放锁
 atomic.StoreInt64(&m.owner, -1)
 m.Mutex.Unlock()
}

func main() {
 var mutex = &ReentrantMutex{}
 mutex.Lock()
 mutex.Lock()
 fmt.Println(111)
 mutex.Unlock()
 mutex.Unlock()
}

最后给自己的原创 Go 面试小册打个广告,如果你从事 Go 相关开发,欢迎扫码购买,目前 10 元买断,加下面的微信发送支付截图额外赠送一份自己录制的 Go 面试题讲解视频

7a596ecd941dfb193a4cab58a4fea290.jpeg

aa87b1ed6b931ec6b3c1e9355c50062f.png

如果对你有帮助,帮我点一下在看或转发,欢迎关注我的公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值