Vector单个方法虽synchronized,但组合操作不具原子性,因此push_back()、pop_back()、resize()、clear()及越界下标赋值等写操作或读写混合场景必须加锁。

vector 的哪些操作必须加锁
不是所有 vector 操作都线程安全,只要多个线程同时做「写」或「读+写」,就必须加锁。典型必须加锁的场景包括:
• push_back()、pop_back()、resize()、clear()
• 用下标赋值(v[i] = x)且 i 超出当前 size()(触发 reallocation)
• 任何调用 begin()/end() 后,另一线程修改了容器(迭代器可能失效)
注意:只读访问(如 v[i] 或 v.at(i),且确保 i )在无写操作时可不加锁,但无法靠代码静态保证——所以实践中,只要存在并发写,所有访问都建议统一加锁。
用 std::mutex 保护 vector 的常见写法
最直接的方式是把 vector 和 std::mutex 绑定在一起,避免忘记加锁。推荐封装成结构体或类:
struct ThreadSafeVector {
std::vector<int> data;
mutable std::mutex mtx;
void push(int x) {
std::lock_guard<std::mutex> lock(mtx);
data.push_back(x);
}
int get(size_t i) const {
std::lock_guard<std::mutex> lock(mtx);
return data.at(i); // at() 带边界检查,比 [] 更安全
}
};
关键点:
• mutable 允许 const 成员函数(如 get())也能加锁
• 用 std::lock_guard 自动管理锁生命周期,避免手写 unlock() 遗漏
• 不要直接暴露 data 成员,否则外部绕过锁直接调用 push_back() 就崩了
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
为什么不能只锁部分操作(比如只锁 push_back)
看似只写入要加锁,但实际容易踩坑:
• push_back() 可能触发内存重分配,导致所有已有元素地址变化,其他线程正在遍历的迭代器/指针立刻失效
• 一个线程在调用 size() 后,另一个线程执行 push_back(),前者接着用旧 size 当作上界访问,越界读写
• 即使只读线程也依赖 size()、data() 等返回值,这些函数本身不修改数据,但结果有效性依赖容器状态一致性
所以「读写分离」在这里不可靠;除非你用 std::shared_mutex(C++17)配 shared_lock + unique_lock,但要注意 shared_mutex 在 Windows 上某些旧 STL 实现(如 MSVC 2015)不完全支持,且性能未必比普通 mutex 好。
立即学习“C++免费学习笔记(深入)”;
替代方案:什么时候该换容器而不是硬加锁
如果频繁并发读写、且对性能敏感,std::vector 加锁往往成为瓶颈。这时考虑:
• 写少读多 → 用 std::shared_mutex + vector,但需确认编译器和 STL 支持
• 多生产者多消费者 → 改用无锁队列(如 moodycamel::ConcurrentQueue)或 std::queue 配单个 mutex
• 需要随机访问 + 并发修改 → 考虑分段锁(把 vector 拆成多个子 vector,各自配 mutex),但实现复杂、边界逻辑易错
• 完全避免共享 → 每个线程用独立 vector,最后再合并,适合 map-reduce 类场景
别为了“用 vector”而强行加锁——锁是兜底手段,不是设计首选。

















