C++构造函数线程安全性
C++ constructor thread safety
假设我在构造函数中初始化了一个成员变量向量,并且该向量在其他几个成员函数中被读取(而不是写入其他任何地方)。我是否需要保护对向量(包括在构造函数中)的访问,或者是否保证对象在其他线程中使用之前将被完全初始化并刷新到主内存?
让我举个例子:
class A
{
public:
A();
void f1();
void f2();
private:
std::vector<int> v;
};
A::A()
{
// do some setup work in v
v.push_back(1);
}
// called from thread1
void A::f1()
{
// some readonly work on v
for (auto i : v) {
// do something on i
}
}
// called from thread2
void A::f2()
{
// more readonly work on v
if (v.empty()) {
// do other work
}
}
我需要在A::A()
、A::f1()
和A::f2()
中锁定保护 v 吗?
对象由单个线程创建,因此在接触成员变量的构造函数中运行代码时,永远不必担心线程安全性。但是,如果在构造函数中使用静态变量,则可能需要在访问周围添加某种形式的锁定。
在一种边缘情况下,构造函数中的代码可以由多个线程调用,这是当您使用任一placement new
时。例如,假设您在某处有一个缓冲区,并且您要在其中分配一个对象:
byte buffer[100];
Foo *foo = new (buffer) Foo;
在这里,除非锁定对new
的调用,否则两个或多个构造函数可能会并行运行,因为它们针对同一内存块运行。但是,这是一个真正的专用边缘情况,需要特殊处理(例如锁定放置在新结构周围)。
对象由单个线程构造。其他线程只能通过实例引用访问该对象。换句话说,对象的构造函数将在其他线程调用方法之前完成其工作。因此,无需在构造函数中实现线程安全代码。
当然,如果另一个对象作为参数传递给构造函数,则在构造函数中对该对象的最终访问应该是线程安全的。
如其他答案所述,在构造函数中实现同步原语是没有意义的,但这并不意味着如果你不从外部同步,你就不能有比赛:
std::atomic<A*> g_ptr = nullptr;
void threadFun1() {
g_ptr.store(new A{}, std::memory_order_relaxed);
}
void threadFun2() {
A* l_ptr = nullptr;
while (l_ptr == nullptr) {
l_ptr = g_ptr.load(std::memory_order_relaxed);
}
l_ptr->f1();
}
在上面的代码中,您在 A
和 f1
的构造函数之间进行数据竞争。问题是 - 如果没有同步 - 从thread2的角度来看,g_ptr可能在对象完全构造之前写入。
但是,在构造函数内部,您无法执行任何操作来防止这种比赛。相反,您必须使用外部同步方式,例如对原子加载和存储操作使用非宽松内存排序,或者在设置全局变量后从 thread1 中启动 thread2。
以下面的代码为例:
型号.h
namespace Stackoverflow {
class Model {
public:
Model();
~Model();
std::vector<int> *integers() const { return _integers.get(); }; // read only
private:
std::unique_ptr<std::vector<int>> _integers; // registered before constructor
};
}
型号.cpp
Stackoverflow::Model::Model() {
_integers = std::make_unique<std::vector<int>>(); // initialized
}
Stackoverflow::Model::~Model() {
_integers.release();
}
私有成员"_integers"将被注册,但在调用方调用构造函数之前不会初始化。
Stackoverflow::Model stackoverflow;
当另一个线程想要访问此向量时,调用 getter。
auto *vector = stackoverflow.integers();
当调用方实际请求向量时,成员将被完全初始化。
- 类与私有变量的其他类之间的线程安全性
- 调用socket.remote_endpoint(提升 asio 库)线程安全性
- std::lock_guard 似乎提供了线程安全性,尽管作用域块
- C++中向量和列表的非写入成员函数的线程安全性
- 线程安全性和静态变量/成员功能
- 是仅使用get或toplown的原始类型的线程安全性的威胁
- 提高 ASIO stream_descriptor和事件 FD 线程安全性
- 是boost :: asio :: thread_pool线程在多个线程上发布任务时的安全性
- OpenMP中树结构的线程安全性
- 静态变量初始化的线程安全性
- c++线程的安全性和时间效率:为什么有互斥检查的线程有时比没有它的线程工作得更快
- 从另一个(非 qt)线程调用 QObject 方法的线程安全性?
- C 共享_ptr如何确保线程安全性
- 编写std::vector与普通数组的线程安全性
- C++标准库容器相对于所包含对象的线程安全性
- 读取和写入操作的线程安全性C++
- Qt库-静态成员函数的线程安全性
- QObject可重入性和线程安全性
- std::map中的线程安全性
- boost::asio io_service 和 std::containers 的线程安全性