std::mutex是 C 标准库自 C11 起引入中用于多线程编程的核心组件位于mutex头文件中。它代表互斥量Mutual Exclusion用于保护共享数据防止多个线程同时访问导致的数据竞争Data Race。以下是关于std::mutex的核心用法、最佳实践及注意事项1. 基本用法std::mutex提供了两个主要成员函数lock(): 尝试获取锁。如果锁已被其他线程持有当前线程会阻塞直到锁被释放。unlock(): 释放锁。原始用法示例不推荐直接使用见下文“最佳实践”#include iostream #include thread #include mutex std::mutex mtx; int shared_data 0; void increment() { mtx.lock(); // 加锁 shared_data; mtx.unlock(); // 解锁 } int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); std::cout Result: shared_data std::endl; return 0; }2. 最佳实践使用 RAII 锁守卫手动调用lock()和unlock()容易出错例如在锁持有期间发生异常导致unlock()未被调用造成死锁。强烈建议使用 RAII资源获取即初始化机制的锁守卫类std::lock_guard(C11)最简单的锁守卫。构造时加锁析构时自动解锁。适用于整个作用域都需要锁的情况。#include mutex std::mutex mtx; void safe_increment() { std::lock_guardstd::mutex lock(mtx); // 构造时加锁 // 临界区代码 // 函数退出无论正常返回还是抛出异常时lock 析构自动解锁 }std::unique_lock(C11)比lock_guard更灵活。支持延迟锁定、手动解锁、尝试锁定以及配合条件变量 (std::condition_variable) 使用。#include mutex std::mutex mtx; void flexible_increment() { std::unique_lockstd::mutex lock(mtx); // ... 做一些不需要锁的操作 ... lock.unlock(); // 可以手动提前解锁 // ... 做一些不需要锁的操作 ... lock.lock(); // 也可以重新加锁 }3. 重要特性与限制不可复制/移动:std::mutex对象本身不能被复制或移动。这意味着包含std::mutex的类也不能默认生成拷贝构造函数或拷贝赋值运算符。非递归性:std::mutex是非递归的。如果一个已经持有该锁的线程再次尝试lock()同一个 mutex会导致死锁Deadlock。解决方案: 如果需要递归锁请使用std::recursive_mutex。所有权:std::mutex不记录拥有者线程 ID。虽然通常由加锁的线程解锁但标准并未强制检查不过在不同线程间传递锁通常逻辑上是错误的。4. 常见变体类型描述适用场景std::mutex标准互斥量独占访问。绝大多数临界区保护。std::recursive_mutex可递归互斥量。同一线程可多次加锁。必须在同一线程内递归调用受保护函数时尽量避免设计成这样。std::timed_mutex带超时的互斥量。支持try_lock_for和try_lock_until。需要避免无限等待尝试获取锁失败后执行其他逻辑时。std::recursive_timed_mutex可递归且带超时的互斥量。上述两者的结合。std::shared_mutex(C17)共享互斥量读写锁。允许多个读者或一个写者。读多写少的场景提高并发读取性能。5. 典型错误示例死锁示例自锁void bad_function(std::mutex m) { std::lock_guardstd::mutex lock1(m); // 以下代码会导致死锁因为当前线程已经持有了 m std::lock_guardstd::mutex lock2(m); }异常安全示例展示为什么需要 lock_guardvoid risky_function(std::mutex m) { m.lock(); if (some_condition) { throw std::runtime_error(Error!); // 如果没有 lock_guardm.unlock() 永远不会被执行锁泄露 } m.unlock(); }使用std::lock_guard则无需担心此问题。总结在现代 C 开发中始终包含mutex。优先使用std::lock_guard管理std::mutex。仅在需要配合条件变量或需要精细控制锁生命周期时使用std::unique_lock。尽量避免手动调用lock()和unlock()。
std::mutex
std::mutex是 C 标准库自 C11 起引入中用于多线程编程的核心组件位于mutex头文件中。它代表互斥量Mutual Exclusion用于保护共享数据防止多个线程同时访问导致的数据竞争Data Race。以下是关于std::mutex的核心用法、最佳实践及注意事项1. 基本用法std::mutex提供了两个主要成员函数lock(): 尝试获取锁。如果锁已被其他线程持有当前线程会阻塞直到锁被释放。unlock(): 释放锁。原始用法示例不推荐直接使用见下文“最佳实践”#include iostream #include thread #include mutex std::mutex mtx; int shared_data 0; void increment() { mtx.lock(); // 加锁 shared_data; mtx.unlock(); // 解锁 } int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); std::cout Result: shared_data std::endl; return 0; }2. 最佳实践使用 RAII 锁守卫手动调用lock()和unlock()容易出错例如在锁持有期间发生异常导致unlock()未被调用造成死锁。强烈建议使用 RAII资源获取即初始化机制的锁守卫类std::lock_guard(C11)最简单的锁守卫。构造时加锁析构时自动解锁。适用于整个作用域都需要锁的情况。#include mutex std::mutex mtx; void safe_increment() { std::lock_guardstd::mutex lock(mtx); // 构造时加锁 // 临界区代码 // 函数退出无论正常返回还是抛出异常时lock 析构自动解锁 }std::unique_lock(C11)比lock_guard更灵活。支持延迟锁定、手动解锁、尝试锁定以及配合条件变量 (std::condition_variable) 使用。#include mutex std::mutex mtx; void flexible_increment() { std::unique_lockstd::mutex lock(mtx); // ... 做一些不需要锁的操作 ... lock.unlock(); // 可以手动提前解锁 // ... 做一些不需要锁的操作 ... lock.lock(); // 也可以重新加锁 }3. 重要特性与限制不可复制/移动:std::mutex对象本身不能被复制或移动。这意味着包含std::mutex的类也不能默认生成拷贝构造函数或拷贝赋值运算符。非递归性:std::mutex是非递归的。如果一个已经持有该锁的线程再次尝试lock()同一个 mutex会导致死锁Deadlock。解决方案: 如果需要递归锁请使用std::recursive_mutex。所有权:std::mutex不记录拥有者线程 ID。虽然通常由加锁的线程解锁但标准并未强制检查不过在不同线程间传递锁通常逻辑上是错误的。4. 常见变体类型描述适用场景std::mutex标准互斥量独占访问。绝大多数临界区保护。std::recursive_mutex可递归互斥量。同一线程可多次加锁。必须在同一线程内递归调用受保护函数时尽量避免设计成这样。std::timed_mutex带超时的互斥量。支持try_lock_for和try_lock_until。需要避免无限等待尝试获取锁失败后执行其他逻辑时。std::recursive_timed_mutex可递归且带超时的互斥量。上述两者的结合。std::shared_mutex(C17)共享互斥量读写锁。允许多个读者或一个写者。读多写少的场景提高并发读取性能。5. 典型错误示例死锁示例自锁void bad_function(std::mutex m) { std::lock_guardstd::mutex lock1(m); // 以下代码会导致死锁因为当前线程已经持有了 m std::lock_guardstd::mutex lock2(m); }异常安全示例展示为什么需要 lock_guardvoid risky_function(std::mutex m) { m.lock(); if (some_condition) { throw std::runtime_error(Error!); // 如果没有 lock_guardm.unlock() 永远不会被执行锁泄露 } m.unlock(); }使用std::lock_guard则无需担心此问题。总结在现代 C 开发中始终包含mutex。优先使用std::lock_guard管理std::mutex。仅在需要配合条件变量或需要精细控制锁生命周期时使用std::unique_lock。尽量避免手动调用lock()和unlock()。