CPU乱序执行会影响C++中new运算符的顺序吗?

问题描述 投票:0回答:2

C++ 中的 new 运算符执行以下操作:

  1. 分配内存:它在堆上为单个对象或对象数组分配内存。分配的内存量足以容纳指定类型的对象。

  2. 初始化对象:分配内存后,new 通过调用对象的构造函数来初始化对象。对于单个对象,它直接调用构造函数。对于对象数组,它为数组中的每个对象调用构造函数。

  3. 返回指针:它返回对象类型的指针,该指针指向存储对象的已分配内存的第一个字节。对于数组,它返回指向数组第一个元素的指针。

是否已返回指针,但构造函数尚未完全执行且CPU乱序执行

class Singleton
{

private:
    static Singleton * pinstance_;
    static std::mutex mutex_;

protected:
    Singleton()
    {
      //
    }
    ~Singleton() {}

public:

    Singleton(Singleton &other) = delete;

    void operator=(const Singleton &) = delete;

    static Singleton *GetInstance();
};

Singleton* Singleton::pinstance_{nullptr};
std::mutex Singleton::mutex_;

Singleton *Singleton::GetInstance(const std::string& value)
{
    if (pinstance_ == nullptr)
    {
        std::lock_guard<std::mutex> lock(mutex_);
        if (pinstance_ == nullptr)
        {
           pinstance_ = new Singleton();
        }
    }
    return pinstance_;
}

线程A拿到锁后执行new操作符,并没有完成构造函数,但是指针已经指向了申请的内存。这时线程B进来,在第一次if判断中发现指针不为NULL,并使用该指针进行后续处理

在多线程的情况下,上述情况也可能吗?

c++ singleton cpu cpu-architecture new-operator
2个回答
2
投票

如果您根据 C++ 标准编写具有明确定义行为的 C++ 代码,那么您无需担心指令“乱序”执行。特别是,标准保证在new-expression中,首先调用分配函数,然后,如果分配成功,则初始化对象,并且只有在初始化成功后,new-expression具有一个值,并且该值可以被程序进一步使用。因此,您可以假设您的编译器将生成使程序根据此事件序列运行的代码。

在多线程程序中,C++ 标准提供的保证较弱。多线程程序中的“数据竞争”会导致程序出现未定义的行为,并且观察到的程序行为可能会使指令看起来像是无序执行的,甚至可能会出现更不可预测的结果。而且,即使在没有数据竞争的多线程程序中,也不能保证当线程 A 观察到线程 B 引起的副作用时,线程 A 也能看到线程 B 引起的所有先前副作用,除非进行适当的同步操作被使用。同样,如果您需要特定的行为,则您有责任使用标准提供的同步操作,根据 C++ 标准编写提供该行为的代码。如果你这样做,你就不需要考虑机器层面发生了什么。


1
投票

CPU 的 as-if 规则与从 C++ 源代码生成 asm 的编译器的 as-if 规则几乎是相同的概念,但对于 CPU 来说,“可观察的行为”是由 ISA 的内存模型定义的,而不是 C++ 标准.

© www.soinside.com 2019 - 2024. All rights reserved.