如何解决递归死锁的问题
什么是递归死锁问题?
假设一种情况,函数A需要操作某个变量,所以需要获取这个变量的锁,同样地函数B也要操作这个变量,所以也要获取这个锁。 但在某种需求下,函数A内部需要调用函数B,此时会因为A已经获得了锁,B永远得不到锁而导致递归死锁。
一个比较简单的解决方法是:创建一个无锁版本的函数B提供给函数A调用,如:
func B {
// 正常版本,需要上锁
l.lock()
UnlockB()
l.unlock()
}
func UnlockB {
// 无锁版本
// do something...
}
func A {
l.lock()
UnlockB()
l.unlock()
}
但我觉得这个方法可能没那么“优雅”,虽然它能快速解决问题。下面我们来了解一下更加高性能或者代码冗余更少的方法。
可重入锁
可重入锁内部会维持一个计数器和一个变量,如果是同一个线程(线程信息保存在变量中)重复上锁,则计数器+1,而不是直接阻塞。这样能有效解决在嵌套调用的函数中对同一个互斥量上锁的问题。
可重入锁的优势是代码改动小、修复快速。
按照单一职责重构
多个函数关心同一个锁及其保护的变量,这可能违反了”单一职责原则”。 我们可以创建一个管理类,让这个类来负责变量重入的控制,并提供业务接口给其他函数使用。
这个方法可以从根本上提升代码质量,有别于增加一个 无锁内部函数UnlockB。
更细粒度的锁或无锁化
常见方法有:
- 读写锁
- 原子变量
- CAS
这类方法可能实现起来更复杂,但是有更加好的性能表现。