繁体   English   中英

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();
}

在上面的代码中,您在Af1的构造函数之间进行了数据竞争。 问题是-从线程2的角度来看-在没有同步的情况下,可能在对象完全构建之前编写了g_ptr。

但是, 在构造函数内部您无法采取任何措施来防止这种竞争。 相反,您必须使用外部同步方式,例如对原子加载和存储操作使用非宽松的内存排序,或者在设置全局变量后从thread1内部启动thread2。

请使用以下代码示例:

型号

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
    };
}

模型

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();

当调用者实际请求引导程序时,将完全初始化该成员。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM