C++ 互斥锁(Mutex)与临界区(Critical Section):std::mutex 与 std::lock_guard C 互斥锁Mutex与临界区Critical Sectionstd::mutex 与 std::lock_guardflyfish先看没有锁会怎样#includeiostream#includethread#includevectorintcounter0;// 两个线程同时 数据竞争intmain(){std::vectorstd::threadthreads;for(inti0;i10;i){threads.emplace_back([]{for(intj0;j10000;j){counter;// 不是原子操作多个线程同时执行会丢更新}});}for(autot:threads)t.join();std::coutcounter counter\n;// 期望 100000实际小于它return0;}counter 的 分三步读旧值、加一、写回。两个线程同时读到同一个旧值各加一次再写回其中一次更新就丢了。数据竞争的结果是未定义行为数值不对只是最常见的表现。术语对照表中文术语英文术语定义临界区Critical Section访问共享资源的代码段同一时刻只允许一个线程进入互斥锁 / 互斥量Mutex (Mutual Exclusion)保证临界区互斥访问的同步原语锁管理RAII 包装Lock Guard / RAII Wrapper构造时加锁、析构时解锁的自动管理机制数据竞争Data Race多个线程同时访问同一内存且至少一个为写操作且无同步机制死锁Deadlock多个线程互相持有对方所需锁而永久阻塞的状态锁粒度Lock Granularity临界区覆盖代码范围的大小细粒度代表更小的临界区未定义行为Undefined Behavior (UB)标准未规定结果的行为数据竞争即属此类一、概述互斥锁Mutex是最基础的同步原语其职责是保证**临界区Critical Section**的互斥访问任意时刻临界区至多允许一个线程进入其余线程在锁上阻塞等待。std::mutex 是 C 标准库对互斥锁的正式实现。std::lock_guard 是它的标准配套管理工具用于以 RAII 方式自动释放锁消除手动解锁引入的死锁风险。二、原理2.1 互斥性互斥锁维护一个逻辑状态已锁定locked或未锁定unlocked。线程在进入临界区前必须先获得锁持锁期间其他线程的加锁请求被阻塞直到持锁线程解锁。2.2 数据竞争的本质未加锁时对共享变量的读改写操作可能被其他线程交错执行。以自增操作为例其底层包含读取、修改、写回三个步骤。两个线程同时执行时可能读取同一旧值各自写回导致一次更新丢失。C 标准将此类无同步的数据竞争定义为未定义行为其结果不可预测。三、接口规范std::mutex 提供三个成员方法接口语义阻塞行为lock()获取锁阻塞直至获得锁unlock()释放锁无阻塞持锁线程调用try_lock()尝试获取锁非阻塞未获得立即返回 false四、使用方式4.1 不推荐手动加锁std::mutex mtx;intcounter0;voidincrement(){mtx.lock();counter;mtx.unlock();// 必须保证所有执行路径都调用否则死锁}手动加锁存在两类固有风险其一分支提前返回导致解锁语句被跳过锁永久持有其余线程无限阻塞其二临界区抛出异常时解锁语句不执行同样导致锁泄漏。这两类风险均源于解锁依赖程序员在每条路径上手动执行。4.2 推荐RAII 自动管理#includemutexstd::mutex mtx;intcounter0;voidincrement(){std::lock_guardstd::mutexlock(mtx);// 构造加锁counter;// 临界区}// 析构无条件解锁std::lock_guard 构造时调用 lock()析构时调用 unlock()。C 保证局部对象在作用域退出时析构无论退出路径为正常返回、提前 return 或异常传播。解锁因此成为编译器保证执行的确定性行为。4.3 临界区粒度控制临界区应保持最小化仅覆盖对共享资源的访问不包含与共享状态无关的耗时计算std::mutex mtx;Data shared_data;voidprocess(){Data snapshot;{std::lock_guardstd::mutexlock(mtx);snapshotshared_data;// 临界区仅限此行}expensive_computation(snapshot);// 锁外执行不阻塞其他线程}std::lock_guard把解锁绑定到析构lock_guard 的原理只有一句话构造时加锁析构时解锁中间什么都不管。#includeiostream#includemutex#includethread#includevectorintcounter0;std::mutex mtx;voidaddOne(){std::lock_guardstd::mutexlock(mtx);// 构造加锁counter;// 临界区// 析构无论怎么离开这里都会解锁}intmain(){std::vectorstd::threadthreads;for(inti0;i10;i){threads.emplace_back([]{for(intj0;j10000;j)addOne();});}for(autot:threads)t.join();std::coutcounter counter\n;// 稳定输出 100000return0;}