c++ - C++ 中的内存模型和单例

标签 c++ memory singleton sequential consistency

在关于并行编程的讲座中,我们被告知不应再使用 C++ 中这种用于单例的旧线程安全模式:

class A {
 public:
  static A* instance() {
    if (!m_instance) {
      std::lock_guard<std::mutex> guard(m_instance_mutex);
      if (!m_instance)
        m_instance = new A();
    }
    return m_instance;
   }
 private:
  A()
  static A* m_instance;
  static std::mutex m_instance_mutex;
 }

这是因为不能保证没有干净的内存模型,下面的步骤没有明确的顺序: 1.为A分配内存 2.初始化对象A 3. 使 m_instance 指向那个内存

例如3 之后可能会从 2 重新排序:m_instance 可能已经指向那里,但没有有效的对象。另一个线程现在可以看到一个非零指针,但对无效数据进行操作。

这就是为什么我们应该使用放置内存栅栏的 Meyer 单例。

但我不确定为什么不能保证这些步骤的顺序:我认为 C++ 和 Java 使用顺序一致性内存模型,它不允许任何类型的 StoreLoad/LoadStore/StoreStore/LoadLoad 重新排序。即使使用 Total Store Order,允许 StoreLoad 重新排序,为什么 2 和 3 可以交换?

最佳答案

更新:我认为这适用:

来自:https://en.cppreference.com/w/cpp/language/eval_order

...8) The side effect (modification of the left argument) of the built-in assignment operator and of all built-in compound assignment operators is sequenced after the value computation (but not the side effects) of both left and right arguments, and is sequenced before the value computation of the assignment expression (that is, before returning the reference to the modified object)..."

如果“副作用”包含调用构造函数;然后编译器可以在调用构造函数之前将原始内存的地址存储在 m_instance 中。然后第二个线程会认为 m_instance 已完全构建。

自 C++11 以来,以下是线程安全的:

class A
{
  private:
    A() {};

  public:
    A& get_instance()
    {
      static A instance;
      return instance;
    }
};

Variables declared at block scope with the specifier static have static storage duration but are initialized the first time control passes through their declaration...

If multiple threads attempt to initialize the same static local variable concurrently, the initialization occurs exactly once...

来自:https://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables

关于c++ - C++ 中的内存模型和单例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57738586/

相关文章:

c++ - 专业游戏开发环境中的最佳 C++ 编程实践?

c - 释放链表中的节点

c++ - 为什么还用C语言呢?

c++ - 痛饮 : Unable to access constructor with double pointer

memory - Golang追加内存分配VS。 STL push_back 内存分配

android - 在 Android 中将服务用作单例

ios - iOS 中的多个委托(delegate)

ios - 在 Singleton 类中使用数组在 View Controller IOS 之间传递数据?

c++ - 创建与 nginx 通信的 c++ 应用程序的最佳方法

C:使用指针作为字符串:不可预知的行为