C++C++ 11C++11 std::mutex
枫C++11 std::mutex
多线程程序里最容易出问题的地方之一,就是多个线程同时读写共享数据。
std::mutex 就是 C++11 提供的最基础互斥锁,用来保护临界区。
1. 为什么需要互斥锁?
来看一个简单场景:两个线程同时给同一个计数器加一。
如果没有同步保护,结果就可能出现竞态条件,最终值未必正确。
2. 基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <thread> #include <mutex> #include <iostream> using namespace std;
std::mutex mtx; int counter = 0;
void work() { for (int i = 0; i < 1000; ++i) { mtx.lock(); ++counter; mtx.unlock(); } }
int main() { thread t1(work); thread t2(work);
t1.join(); t2.join();
cout << counter << endl; return 0; }
|
3. 更推荐的写法:lock_guard
手动 lock() / unlock() 虽然能用,但不够稳,尤其一旦中间出现异常或提前返回,就容易忘记解锁。
更推荐这样写:
1 2 3 4 5 6 7
| void work() { for (int i = 0; i < 1000; ++i) { std::lock_guard<std::mutex> lock(mtx); ++counter; } }
|
这就是典型的 RAII 风格。
4. 一个更完整的示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <thread> #include <mutex> #include <vector> #include <iostream> using namespace std;
mutex mtx; int total = 0;
void addTask() { for (int i = 0; i < 10000; ++i) { lock_guard<mutex> lock(mtx); ++total; } }
int main() { vector<thread> threads; for (int i = 0; i < 4; ++i) { threads.emplace_back(addTask); }
for (auto& t : threads) { t.join(); }
cout << total << endl; return 0; }
|
5. 使用时要注意的点
5.1 锁的范围尽量小
锁持有时间越长,线程阻塞越明显。
所以通常应只把真正需要保护的代码放进临界区。
5.2 避免重复加锁导致死锁
如果同一线程在不合适的场景下重复拿同一把锁,程序可能会卡死。
5.3 不要忘记 join 线程
锁解决的是共享数据同步问题,线程本身的生命周期管理还是要自己处理好。
总结
std::mutex 是 C++11 并发编程里最基础的一把锁。
它不复杂,但非常重要。实际开发里,通常会配合 std::lock_guard 或 std::unique_lock 使用,这样比手动加解锁更安全。