C++11 recursive_mutex

本文详细介绍了C++11中recursive_mutex的特性与使用,包括线程如何获得和释放锁,以及在递归锁定场景下其他线程的行为。同时,讨论了递归锁在实际编程中可能遇到的问题,如多个函数误以为各自持有锁而导致的数据同步错误。

一 简介

头文件<mutex>

class recursive_mutex; c++11

1. 调用线程从成功调用 lock 或 try_lock 开始占有recursive_mutex, 期间线程可以进行对 lock 或 try_lock的附加调用,所有权在线程调用 unlock 匹配次数时结束。

2. 线程占有recursive_mutex时,若其他线程要求recursive_mutex所有权,调用lock将被阻塞,调用try_lock将返回false.

3. 可锁定recursive_mutex的最大次数未指定的,但到达该数后,对 lock 的调用将抛出 std::system_error 而对 try_lock 的调用返回false;

4. 若recursive_mutex在仍被线程占有时被销毁,则程序行为未定义。recursive_mutex满足 mutex 和 标准布局类型的所有要求。

二 举例

M m;

void f()
{
  m.lock();
  // do sth
  m.unlock();
}

void g()
{
  m.lock();
  f();
  // do sth
  m.unlock();
}

当M为非递归锁时,则程序会死锁; 如果M是递归锁能通过。但使用递归锁容易出现的问题是 f 和 g 均以为自己拿到了锁,然后却修改了同个对象,问题很难发现。

三 参考

cppreference

<think>首先,用户要求使用中文回答问题。问题是要详细讲解recursive_mutexC++中的实用场景。 recursive_mutexC++标准库中的一种互斥量,它允许同一个线程多次定同一个互斥量,而不导致死。这与普通的mutex不同,普通的mutex如果同一个线程尝试再次定它,会导致未定义行为或死。 实用场景: 1. **递归函数**:当一个函数调用自身时,如果该函数需要定一个互斥量,recursive_mutex就很有用。例如,在递归算法中保护共享资源。 2. **可重入函数**:如果一个函数可能被多个线程调用,但也可能被同一个线程递归调用,使用recursive_mutex可以避免死。 3. **对象内部同步**:在一个类中,如果多个成员函数都需要定同一个互斥量,并且这些函数可能相互调用recursive_mutex可以确保安全。 4. **回调函数**:在事件处理或回调中,如果一个回调函数可能触发另一个回调,并且它们都需要访问共享资源。 关键点:recursive_mutex增加了开销,因为它需要跟踪定次数,所以只在必要时使用。普通mutex更高效。 现在,我需要提供代码示例。用户没有指定语言,但问题涉及C++,所以我会用C++代码。 代码示例:展示recursive_mutex的使用。 ```cpp #include <iostream> #include <thread> #include <mutex> #include <vector> std::recursive_mutex rmutex; void recursive_function(int n) { std::lock_guard<std::recursive_mutex> lock(rmutex); if (n > 0) { std::cout << "Level " << n << std::endl; recursive_function(n - 1); // 递归调用,同一个线程再次定 } } int main() { std::thread t1(recursive_function, 3); std::thread t2(recursive_function, 2); t1.join(); t2.join(); return 0; } ``` 在这个例子中,recursive_function使用recursive_mutex,允许它在递归调用中安全地定同一个互斥量。 现在,回答完问题后,我必须根据用户的问题及我的回答提出3-5个相关问题。这些必须以“§§相关问题§§”开头,并且不能换行分散。问题列表在最后。 相关问题应该与主题相关。可能的想法: 1. recursive_mutex与普通mutex的区别是什么? 2. 在什么情况下应该避免使用recursive_mutex? 3. 如何实现一个线程安全的递归数据结构? 4. C++中还有其他同步机制吗? 5. recursive_mutex的性能影响如何? 确保3-5个问题。 最后,输出格式:回答内容,然后“§§相关问题§§”后跟问题列表。 在回答中,我需要详细讲解实用场景,所以我会先写解释,然后代码,最后相关问题。 确保所有内容符合规则。</think>### 详细讲解 recursive_mutexC++ 中的实用场景 在 C++ 中,`std::recursive_mutex` 是一种特殊的互斥量(mutex),它允许同一个线程多次定同一个互斥量,而不会导致死或未定义行为。这与普通的 `std::mutex` 不同,后者在同一个线程尝试重复定时会引发问题(如死或抛出异常)。`recursive_mutex` 通过内部计数器跟踪定次数:每次定递增计数器,解时递减,只有当计数器归零时,其他线程才能获取。 #### 为什么需要 recursive_mutex? - **普通 mutex 的局限性**:如果一个线程已经定了一个普通 mutex,再次尝试定会导致阻塞(死)或异常(如 `std::system_error`)。 - **recursive_mutex 的优势**:它支持“可重入性”(reentrancy),即同一个线程可以递归地访问受保护的代码区域,这在某些复杂逻辑中非常必要。 #### 主要实用场景 以下是 `recursive_mutex` 的典型应用场景,结合代码示例说明: 1. **递归函数调用** 当函数需要递归执行(例如,处理树形结构或分治算法),并且每次调用都需访问共享资源时,`recursive_mutex` 确保线程安全。 **示例代码**: ```cpp #include <iostream> #include <mutex> #include <thread> std::recursive_mutex rmutex; void recursive_print(int depth) { std::lock_guard<std::recursive_mutex> lock(rmutex); // 定互斥量 if (depth > 0) { std::cout << "Depth: " << depth << std::endl; recursive_print(depth - 1); // 递归调用,同一个线程再次定 } } int main() { std::thread t(recursive_print, 3); // 线程递归访问 t.join(); return 0; } ``` **说明**:这里 `recursive_print` 函数递归调用自身,使用 `recursive_mutex` 避免死。如果使用普通 mutex,第二次定会阻塞线程。 2. **对象内部方法相互调用** 当一个类的多个成员函数需要访问共享状态,且这些函数可能相互调用(如 A() 调用 B()),`recursive_mutex` 确保内部同步安全。 **示例代码**: ```cpp #include <iostream> #include <mutex> class SafeCounter { int count = 0; std::recursive_mutex rmutex; public: void increment() { std::lock_guard<std::recursive_mutex> lock(rmutex); count++; } void reset() { std::lock_guard<std::recursive_mutex> lock(rmutex); count = 0; log_reset(); // 内部调用另一个定方法 } void log_reset() { std::lock_guard<std::recursive_mutex> lock(rmutex); // 再次定 std::cout << "Counter reset to 0" << std::endl; } }; int main() { SafeCounter counter; counter.reset(); // 安全调用,不会死 return 0; } ``` **说明**:`reset()` 方法调用 `log_reset()`,两者都定同一个互斥量。`recursive_mutex` 允许这种嵌套定。 3. **回调或事件处理系统** 在事件驱动的架构中(如 GUI 或网络库),一个回调函数可能触发另一个回调,且都需访问共享资源。`recursive_mutex` 处理这种“链式调用”。 **示例代码**: ```cpp #include <iostream> #include <mutex> #include <functional> std::recursive_mutex rmutex; std::function<void()> callback; void handle_event() { std::lock_guard<std::recursive_mutex> lock(rmutex); std::cout << "Handling event" << std::endl; if (callback) callback(); // 可能触发嵌套回调 } int main() { callback = []() { std::lock_guard<std::recursive_mutex> lock(rmutex); // 嵌套定 std::cout << "Callback executed" << std::endl; }; handle_event(); // 主线程执行 return 0; } ``` **说明**:事件处理中,`handle_event` 调用回调函数,两者都定互斥量,`recursive_mutex` 防止死。 4. **复杂状态机的实现** 在状态机或工作流引擎中,状态转换可能涉及多层函数调用,所有层都需同步访问全局状态。 **示例场景**:例如,一个订单处理系统,`process_order()` 调用 `validate()`,后者又调用 `log_activity()`,所有步骤都需线程安全。 #### 使用注意事项 - **性能开销**:`recursive_mutex` 比普通 mutex 开销更大(需维护定计数),只在必要时使用。 - **潜在风险**:过度使用可能导致逻辑错误(如忘记解),建议配合 `std::lock_guard` 或 `std::unique_lock` 实现 RAII。 - **替代方案**:优先考虑重构代码避免递归定(如拆分函数),或用普通 mutex 加线程局部存储(thread-local storage)。 - **适用原则**:仅当同一个线程的嵌套定不可避免时使用,否则普通 mutex 更高效。 总之,`recursive_mutex` 在递归逻辑、对象内部嵌套调用和事件处理中非常实用,但应谨慎使用以避免性能下降。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值