c++的mutex和lock_guard如何使用 互斥锁保护共享资源【多线程】

17次阅读

std::mutex 和 std::lock_guard 是 c++ 线程中安全同步共享资源的核心工具:前者提供手动加锁/解锁接口,后者通过 RaiI 自动管理锁的生命周期,确保异常安全。

c++的mutex和lock_guard如何使用 互斥锁保护共享资源【多线程】

在多线程 C++ 程序中,std::mutexstd::lock_guard 是最常用、最安全的同步工具之一,用于保护共享资源不被多个线程同时修改,避免数据竞争(data race)和未定义行为。

mutex 用来上锁,lock_guard 自动管理锁的生命周期

std::mutex 是一个底层互斥量对象,提供 lock()unlock() 手动控制接口;但手动管理容易出错(比如忘记 unlock、异常提前退出导致死锁)。std::lock_guard 是 RAII 封装器:构造时自动加锁,析构时自动解锁(哪怕发生异常也会释放),非常可靠。

  • 必须用 std::mutex 对象的引用初始化 lock_guard,不能传值
  • lock_guard 是不可复制的(deleted copy constructor),只能移动或上构造

典型用法:保护共享变量或临界区

假设多个线程要累加同一个 int 变量:

std::mutex mtx; int shared_counter = 0;  void increment() {     std::lock_guard guard(mtx); // 构造即加锁     ++shared_counter;                        // 访问共享资源 } // guard 离开作用域,自动调用 ~lock_guard() → 解锁
  • 所有访问 shared_counter 的地方都必须用同一把 mtx 加锁
  • 锁的作用域越小越好,只包裹真正需要保护的代码段
  • 不要跨函数持有锁(比如把 lock_guard 传给另一个函数),会延长临界区,降低并发性

常见错误和注意事项

容易忽略的细节会影响正确性和性能:

立即学习C++免费学习笔记(深入)”;

  • 全局或静态 mutex 要确保初始化顺序安全;推荐直接定义(C++11 起保证静态局部变量线程安全初始化)
  • 避免嵌套加锁同一 mutex(会导致死锁),如已持锁再调 lock() 是未定义行为;可用 std::recursive_mutex(但通常说明设计有问题)
  • 不要把 lock_guard 声明在 if 分支外却只在分支内使用——它必须在需要保护的代码前构造
  • 若需尝试加锁或带超时,用 std::unique_lock 替代,lock_guard 不支持这些操作

完整可运行示例

启动两个线程并发调用 increment() 10 万次,最终结果应为 200000:

#include  #include  #include  #include   std::mutex mtx; int counter = 0;  void increment() {     for (int i = 0; i < 100000; ++i) {         std::lock_guard guard(mtx);         ++counter;     } }  int main() {     std::thread t1(increment);     std::thread t2(increment);     t1.join(); t2.join();     std::cout << "Final counter: " << counter << "n"; // 输出 200000 }

去掉 lock_guardmtx,结果大概率小于 200000,甚至每次不同——这就是典型的竞态现象。

text=ZqhQzanResources