前言
C++中,实现重入锁是确保一个线程可以多次获取同一把锁而不会陷入死锁的机制。C++标准库提供std::recursive_mutex来实现这把锁。
什么是重入锁
重入锁是一种允许同一个线程多次获取同一把锁的机制,而不会陷入死锁。通常情况下,如果一个线程试图多次获取同一把非重入锁,就会导致死锁。然而,重入锁通过维护一个计数器,记录当前线程获取锁的次数,每次释放锁时,计数器减少,直到计数器归零,锁才真正释放。
使用场景
重入锁使用场景如下:
- 递归函数调用: 当一个函数需要递归调用自身,并且每次调用都需要锁时,重入锁可以防止死锁。
- 复杂的代码路径: 当一个锁在多个函数调用路径中被多次获取时,重入锁可以简化锁的管理。
- 类成员函数: 当一个类的成员函数调用另一个成员函数,并且两个函数都需要同一把锁时,重入锁可以确保安全。
使用std::recursive_lock例子
#include <iostream>
#include <thread>
#include <mutex>
class RecursiveLockExample {
public:
RecursiveLockExample() = default;
void functionA(int count) {
if (count <= 0) {
return;
}
rm.lock();
std::cout << "Lock acquired in functionA, count: " << count << std::endl;
functionB(count - 1);
std::cout << "Lock released in functionA, count: " << count << std::endl;
rm.unlock();
}
void functionB(int count) {
if (count <= 0) {
return;
}
rm.lock();
std::cout << "Lock acquired in functionB, count: " << count << std::endl;
functionA(count - 1);
std::cout << "Lock released in functionB, count: " << count << std::endl;
rm.unlock();
}
private:
std::recursive_mutex rm;
};
int main() {
RecursiveLockExample example;
std::thread t1(&RecursiveLockExample::functionA, &example, 5);
t1.join();
return 0;
}
在这个例子中RecursiveLockExample,FuncitonA和FunctionB都是递归函数,并且它们会相互调用。通过使用std::recursive_mutex,确保了同一个线程在递归调用中不会因为多次获取同一个锁而导致死锁。
实现自定义重入锁
虽然std::recursive_mutex已经提供了一个方便的重入锁实现,但我们也可以自己实现一个简单的重入锁。下面是一个基本的自定义重入锁实现:
#include <iostream>
#include <thread>
#include <mutex>
#include <thread>
#include <cassert>
class ReentrantLock {
public:
ReentrantLock() : owner_thread_id(), recursion_count(0) {}
void lock() {
std::thread::id current_thread_id = std::this_thread::get_id();
if (owner_thread_id == current_thread_id) {
// 如果当前线程已经持有锁,递归计数器增加
recursion_count++;
return;
}
// 获取底层互斥锁
m.lock();
// 记录当前线程ID并设置递归计数器为1
owner_thread_id = current_thread_id;
recursion_count = 1;
}
void unlock() {
assert(owner_thread_id == std::this_thread::get_id() && "Trying to unlock a lock held by another thread");
// 减少递归计数器,如果计数器为0,则释放底层互斥锁
if (--recursion_count == 0) {
owner_thread_id = std::thread::id();
m.unlock();
}
}
private:
std::mutex m;
std::thread::id owner_thread_id;
unsigned int recursion_count;
};
void test_function(ReentrantLock& lock, int count) {
if (count <= 0) {
return;
}
lock.lock();
std::cout << "Lock acquired, count: " << count << std::endl;
test_function(lock, count - 1);
std::cout << "Lock released, count: " << count << std::endl;
lock.unlock();
}
int main() {
ReentrantLock lock;
std::thread t1(test_function, std::ref(lock), 5);
t1.join();
return 0;
}
在这个自定义实现中,我们使用一个标准的std::mutex作为底层锁,并用一个std::thread::id来记录持有锁的线程ID。recursion_count用于记录递归获取锁的次数。在lock函数中,如果当前线程已经持有锁,则递归计数器增加。否则,获取底层锁并记录线程ID。在unlock函数中,递归计数器减少,如果计数器为0,则释放底层锁。
总结
重入锁在C++编程中是一个重要的工具,特别是在处理递归函数调用和复杂代码路径时。std::recursive_mutex提供了一个方便的标准实现,而我们也可以根据需要自定义实现重入锁。通过使用重入锁,可以确保同一个线程多次获取同一把锁而不会导致死锁,从而提高代码的安全性和可靠性。