C++标准库中的容器(如std::vector, std::list, std::map等)本身不是线程安全的、不是线程安全的、不是线程安全的,重要的事情讲三遍。这意味着如果你在多线程环境中同时访问(读或写)同一个容器实例,而没有进行适当的同步(如使用互斥锁、原子操作等),那么你的程序可能会遇到数据竞争和不一致的问题。幸运的时候你只是访问到了非预期的错误数据,但大多数情况下程序将会导致异常退出。
具体来说,C++标准库并没有为容器提供内置的线程安全机制。当你从多个线程访问容器时,必须确保:
- 读写分离:在任一时刻,要么只有一个线程在写入容器,要么所有线程都在读取容器,但不能同时有读和写操作。
- 使用同步机制:通过互斥锁(如std::mutex)、读写锁(如std::shared_mutex,C++17及以后)、条件变量、原子操作等同步机制来协调不同线程对容器的访问。
虽然C++标准库容器本身不是线程安全的,但我们可以使用类似 std::mutex 互斥锁来控制对容器的访问。以下示例展示了如何在多线程环境中安全地修改和访问 std::vector 中的数据。
示例:多线程安全地向 std::vector 添加元素
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
// 定义一个全局的互斥锁
std::mutex g_mtx;
// 定义一个全局的vector
std::vector<int> g_data;
// 线程函数,用于向vector中添加元素
void add_data(int value) {
// 锁定互斥锁
std::lock_guard<std::mutex> lock(g_mtx);
// 安全地向vector中添加元素
g_data.push_back(value);
// lock_guard的析构函数会自动解锁互斥锁
}
int main() {
// 创建并启动10个线程,每个线程向vector中添加一个唯一的值
std::thread threads[10];
for (int i = 0; i < 10; ++i) {
threads[i] = std::thread(add_data, i);
}
// 等待所有线程完成
for (auto& th : threads) {
th.join();
}
// 输出vector的内容
for (int val : g_data) {
std::cout << val << ' ';
}
std::cout << std::endl;
return 0;
}
以上示例我们定义了一个全局的 std::vector g_data 用于存储数据,和一个全局的 std::mutex g_mtx 用于同步对 data 的访问。add_data 函数接收一个 int 类型的参数 value,并使用 std::lock_guard(std::mutex) 来自动管理对 g_mtx 的锁定和解锁。
在 main 函数中,我们创建了一个 std::thread 数组 threads,并循环启动 10 个线程,每个线程调用 add_data 函数并传入一个唯一的值。
通过调用每个线程的 join 方法,等待所有线程完成它们的任务。确保在输出 g_data 的内容之前,所有元素都已被安全地添加到 data 中。
最后,遍历 g_data 并输出其内容。由于使用了互斥锁,可以确信将输出所有线程成功添加的元素。
-End-
#想了解更多精彩内容,关注下方公众号。
本人小杨哥:
超20年C++开发经验,独立软件开发;著名开源产品高并发C++应用服务器MYCP作者;开源企业即时通讯软件Entboost首席架构师;开发有【WordBN字远笔记】等共享软件产品。