11

C++11 的新机器模型允许多处理器系统可靠地工作,wrt。对指令进行重组。

正如Meyers 和 Alexandrescu指出的那样,“简单”的双重检查锁定模式实现在 C++03 中并不安全

Singleton* Singleton::instance() {
  if (pInstance == 0) { // 1st test
    Lock lock;
    if (pInstance == 0) { // 2nd test
      pInstance = new Singleton;
    }
  }
  return pInstance;
}

他们在他们的文章中表明,无论您作为程序员做什么,在 C++03 中,编译器都有太多的自由:允许以一种您无法确定最终只得到一个指令的方式重新排序指令的实例Singleton

我现在的问题是:

  • 新的 C++11 机器模型的限制/定义现在是否限制了指令序列,即上述代码始终可以与 C++11 编译器一起使用?
  • 当使用新的库设施(而不是这里的模拟)时,这个单例模式的安全 C++11 实现现在看起来Lock如何?
4

3 回答 3

5

如果pInstance是常规指针,则代码有潜在的数据竞争——指针(或任何内置类型,就此而言)的操作不能保证是原子的(编辑:或有序)

if pInstanceis anstd::atomic<Singleton*> 并且 Lock内部使用 anstd::mutex来实现同步(例如 if Lockis 实际上std::lock_guard<std::mutex>),代码应该是无数据竞争的。

请注意,您需要显式锁定原子pInstance来实现正确的同步。

于 2011-05-15T15:13:05.397 回答
4

由于静态变量初始化现在保证是线程安全的,所以 Meyer 的单例应该是线程安全的。

Singleton* Singleton::instance() {
  static Singleton _instance;
  return &_instance;
}

现在您需要解决主要问题:您的代码中有一个 Singleton。

编辑:根据我下面的评论:与其他实现相比,此实现有一个主要缺点。如果编译器不支持这个特性会发生什么?编译器会吐出线程不安全的代码,甚至不会发出警告。如果编译器不支持新接口,其他带锁的解决方案甚至都不会编译。这可能是不依赖此功能的一个很好的理由,即使对于单例以外的东西也是如此。

于 2013-09-05T13:22:17.857 回答
1

C++11 不会改变双重检查锁定实现的含义。如果你想进行双重检查锁定工作,你需要建立合适的记忆屏障/栅栏。

于 2011-05-15T13:44:30.063 回答