首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++中的内存模型和单例

C++中的内存模型和单例
EN

Stack Overflow用户
提问于 2019-08-31 21:51:24
回答 2查看 59关注 0票数 4

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

代码语言:javascript
复制
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的Singleton来放置内存栅栏。

但我不确定为什么这些步骤的顺序没有保证:我认为C++和Java利用了顺序一致性内存模型,该模型不允许任何类型的StoreLoad/LoadStore/StoreStore/LoadLoad重新排序。即使是允许StoreLoad重新排序的总商店顺序,为什么2和3可以互换?

EN

回答 2

Stack Overflow用户

发布于 2019-08-31 22:29:16

更新:我认为这适用于:

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

...8)内置赋值运算符和所有内置复合赋值运算符的副作用(左参数的修改)在左参数和右参数的值计算(但不是副作用)之后排序,并在赋值表达式的值计算之前排序(即,在返回对修改的对象的引用之前)...“

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

下面是自C++11以来的线程安全特性:

代码语言:javascript
复制
class A
{
  private:
    A() {};

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

在块作用域中使用说明符

声明的静态变量具有静态存储持续时间,但在控件第一次通过其声明时被初始化...

如果多个线程试图同时初始化同一个静态局部变量,则初始化只发生一次...

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

票数 0
EN

Stack Overflow用户

发布于 2019-09-20 18:36:21

只是猜测,可以使用像atomic_store这样的原子操作来确保步骤2和3的顺序吗?step1可能不需要让m_instance初始化为NULL。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57738586

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档