一、先问一个工程问题
你有一个共享容器:
std::vector<int> data;
两个线程同时往里面 push_back()。
会发生什么?
你可能会说:
本文探讨 C++ 多线程环境下的资源保护机制,重点讲解临界区概念及 std::mutex 的使用。通过对比手动加锁与 RAII 封装的 std::lock_guard,说明异常安全的重要性。文章分析了锁粒度对性能的影响,指出判断与操作需在同一锁内以避免竞态条件,并总结了死锁风险规避原则。同时结合 Java synchronized 关键字进行跨语言对比,强调共享数据必须受保护的核心并发思维。
你有一个共享容器:
std::vector<int> data;
两个线程同时往里面 push_back()。
会发生什么?
你可能会说:
vector 不是自动扩容的吗?
是的,但:
vector 不是线程安全的。
内部会:
如果两个线程同时写:
未定义行为。
并发系统里最重要一句话:
不是'给代码加锁',而是'给资源加保护策略'。
资源 = 被多个线程共享的数据。
临界区是:
访问共享资源的代码区域。
例如:
data.push_back(1);
这一行,就是临界区。
std::mutex mtx;
void task() {
mtx.lock();
data.push_back(1);
mtx.unlock();
}
看起来没问题。
但如果中间抛异常?
mtx.lock();
throw std::runtime_error("error");
mtx.unlock(); // 永远执行不到
锁永远不释放。
其他线程:
永久阻塞(死锁)。
RAII = Resource Acquisition Is Initialization
意思:
资源的获取与对象生命周期绑定。
锁对象在构造时加锁,在析构时自动解锁。
这就是:
std::lock_guard
std::mutex mtx;
void task() {
std::lock_guard<std::mutex> lock(mtx);
data.push_back(1);
}
作用:
无论:
都会释放锁。
Java 写法:
synchronized (lock) {
data.add(1);
}
本质类似:
区别:
| C++ | Java |
|---|---|
| lock_guard 是对象 | synchronized 是语言关键字 |
| 手动控制 mutex | JVM 管理 monitor |
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
std::vector<int> data;
std::mutex mtx;
void task() {
for (int i = 0; i < 10000; ++i) {
std::lock_guard<std::mutex> lock(mtx);
data.push_back(i);
}
}
int main() {
std::thread t1(task);
std::thread t2(task);
t1.join();
t2.join();
std::cout << data.size() << std::endl;
}
输出:
20000
稳定。
你这样写:
void task() {
std::lock_guard<std::mutex> lock(mtx);
for (int i = 0; i < 10000; ++i) {
data.push_back(i);
}
}
锁范围更大。
优点:
缺点:
锁只保护'共享资源操作', 不要把耗时操作放在锁里。
if (!data.empty()) {
std::lock_guard<std::mutex> lock(mtx);
data.pop_back();
}
问题:
data.empty() 没加锁。
可能:
线程 A 判断非空 线程 B pop 完 线程 A 再 pop → 崩溃
正确写法:
std::lock_guard<std::mutex> lock(mtx);
if (!data.empty()) {
data.pop_back();
}
写并发代码前问:
死锁发生在:
例如:
线程 A:锁 m1 → 锁 m2 线程 B:锁 m2 → 锁 m1
工程规避方式:
统一加锁顺序。
✅ 共享数据必须有唯一 mutex ✅ 永远使用 lock_guard(不要手动 lock/unlock) ✅ 判断 + 操作必须在同一锁内 ✅ 锁只保护数据,不保护耗时操作
| 概念 | Java | C++ |
|---|---|---|
| 自动释放锁 | synchronized | lock_guard |
| 手动锁 | ReentrantLock | std::mutex |
| 异常安全 | JVM 保证 | RAII 保证 |
线程私有栈,共享堆; 共享必保护; RAII 是 C++ 锁的灵魂。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online