多线程里共享同一份数据时,不出问题才是小概率事件。比如两个线程同时对一个全局计数器加加,跑出来的结果几乎总是比预期少一截。这不是 bug,是数据竞争,解决它的核心机制就是线程同步。
一个不加锁的典型反例
#include <iostream>
#include <thread>
using namespace std;
int count = 0;
void increment() {
for (int i = 0; i < 100000; ++i) {
count++; // 非原子操作,存在数据竞争
}
}
int main() {
thread t1(increment);
thread t2(increment);
t1.join();
t2.join();
cout << "最终 count 值:" << count << endl;
return 0;
}
不出意外,最终 count 会小于 200000。count++ 看似一行,实际上要经过读、改、写三步,多线程交替执行时这三步就可能被切得七零八落。
C++ 提供的互斥锁
C++11 起 <mutex> 头文件里封装了多种锁,最基础的就是 std::mutex。它只有三个核心成员:
lock():拿锁,拿不到就阻塞等待。unlock():还锁,必须和lock()成对调用。try_lock():试着拿一下,拿不到立刻返回 false,不会卡住线程。
手动调 lock / unlock 很容易出纰漏,比如中间抛出异常就可能跳过 unlock,导致锁永远挂着,其他线程永远等在那——这就是死锁的一种。所以标准库给了个 RAII 包装:std::lock_guard。它在构造时调 ,析构时调 ,保证无论如何都会解锁。


