std::mutex:最基本的互斥量。
std::recursive_mutex:可重入互斥量,允许同一个线程多次加锁。 •std::timed_mutex / std::recursive_timed_mutex:带超时功能的互斥量 (try_lock_for, try_lock_until)。
这三个互斥量(mutex)构成了 c++ 标准库中最基本的同步工具家族。它们的关系可以概括为:std::mutex 是“基础款”,std::recursive_mutex 是“允许重复锁”的变种,而 std::timed_mutex 和 std::recursive_timed_mutex 则是给前两者加上了“超时等待”功能的升级版。
为了让你更直观地理解,可以把它们想象成不同规则的门锁:
- std::mutex:一把只能从外面锁一次的普通门锁。锁上后,任何人(包括锁门的人)想再开,都必须先解锁。
- std::recursive_mutex:一把可以重复上锁的房门。你锁门出去,忘带东西折返,可以直接用钥匙开门进去,不必先解锁。
- std::timed_mutex 和 std::recursive_timed_mutex:则是上述门锁加了“等待时限”功能。你尝试开门时,可以设定只等5秒,超时就不再等,避免被一直卡在门口。
核心区别与联系一览
| 特性 | std::mutex | std::recursive_mutex | std::timed_mutex | std::recursive_timed_mutex |
|---|---|---|---|---|
| 可重复加锁 (同一线程) | ❌ 不允许,再次加锁会导致死锁。 | ✅ 允许,需要相同次数的解锁。 | ❌ 不允许 | ✅ 允许 |
| 超时等待功能 | ❌ 没有,lock() 会一直阻塞。 | ❌ 没有 | ✅ 有 try_lock_for 和 try_lock_until | ✅ 有 try_lock_for 和 try_lock_until |
| 性能开销 | 最低 | 较高(需要维护锁计数) | 较高(需要计时机制) | 最高(功能最复杂) |
| 适用场景 | 绝大多数简单的、非递归的互斥场景。 | 函数递归调用,或函数a调用函数b且两者需锁同一资源。 | 需要避免死等,实现超时重试或取消操作。 | 同时需要递归和超时功能的复杂场景。 |
详细解析与代码示例
1.std::mutex:最基础的互斥量
这是最常用、性能最好的互斥量。但它有一个严格规则:同一个线程不能对它加锁两次。
#include <mutex>
std::mutex mtx;
void func() {
std::lock_guard<std::mutex> lock(mtx);
// 任何在此再次尝试对 mtx 加锁的操作都会导致未定义行为,通常是死锁
}✅ 最佳实践:在绝大多数情况下,优先使用 std::mutex。它足够快,且能迫使你设计出更清晰的锁结构。
2.std::recursive_mutex:允许“重入”的互斥量
它允许同一个线程多次获得同一把锁。内部会维护一个“锁计数器”,每加锁一次+1,每解锁一次-1,直到计数归零,其他线程才能获得锁。
典型场景:在递归函数中保护一个共享资源。
#include <iostream>
#include <mutex>
#include <thread>
std::recursive_mutex rec_mtx;
int shared_data = 0;
void recursive_function(int count) {
if (count <= 0) return;
std::lock_guard<std::recursive_mutex> lock(rec_mtx); // 第二次调用时依然可以加锁成功
shared_data += count;
std::cout << "count: " << count << ", data: " << shared_data << std::endl;
recursive_function(count - 1); // 递归调用,会再次加锁
}
int main() {
std::thread t1(recursive_function, 3);
t1.join();
return 0;
}⚠️ 慎用建议:能用 std::mutex 就尽量不用它。因为它的语义更宽松,容易掩盖糟糕的设计。如果发现需要用递归锁,可以优先考虑重构代码,缩小锁的粒度,或者将递归函数拆分为一个公有接口(加锁)和一个私有实现(不加锁)。
3.std::timed_mutex:带超时功能的互斥量
它继承了 std::mutex 的所有功能,并额外提供了两个成员函数,让你可以“有耐心地等待”:
- try_lock_for(const duration& rel_time):等待一段相对时间。
- try_lock_until(const timepoint& abs_time):等待到一个绝对时间点。
典型场景:避免因某个线程长期占用锁而导致程序卡死,实现优雅的超时重试。
#include <iostream>
#include <mutex>
#include <thread>
#include <chrono>
std::timed_mutex timed_mtx;
void worker() {
// 尝试等待 100 毫秒去获取锁
if (timed_mtx.try_lock_for(std::chrono::milliseconds(100))) {
std::cout << "线程 " << std::this_thread::get_id() << " 获得锁。" << std::endl;
// 模拟工作
std::this_thread::sleep_for(std::chrono::milliseconds(50));
timed_mtx.unlock();
} else {
std::cout << "线程 " << std::this_thread::get_id() << " 等待超时,放弃任务。" << std::endl;
}
}4.std::recursive_timed_mutex:功能最全的组合体
它兼具 recursive_mutex(可重入)和 timed_mutex(超时)的特性,是功能最完整的互斥量,但也是开销最大的。
总结:如何选择?
这是一个清晰的选择路径:
- 首选:std::mutex。功能最纯粹,性能最好,能让代码逻辑最清晰。
- 递归场景:std::recursive_mutex。但需要警惕,使用时可能意味着你的类设计或锁的粒度需要优化。
- 超时场景:std::timed_mutex。常用于需要避免死锁的健壮性系统中。
- 特殊场景:std::recursive_timed_mutex。极少用到,通常在非常复杂的遗留代码或特定算法中才会出现。
到此这篇关于c++ 同步机制 std::mutex的实现示例的文章就介绍到这了,更多相关c++ 同步机制 std::mutex内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论