代码之家  ›  专栏  ›  技术社区  ›  ledonter

在这种单例实现中,两个存储可以重新排序吗?

  •  3
  • ledonter  · 技术社区  · 7 年前

    在下面的单例“get”函数中,其他线程可以看到 instance 不是空的,但是 almost_done 还是假的?(说 差不多完成了 最初 false (第三章)

    Singleton *Singleton::Get() {
        auto tmp = instance.load(std::memory_order_relaxed);
        std::atomic_thread_fence(std::memory_order_acquire);
        if (tmp == nullptr) {
            std::lock_guard<std::mutex> guard(lock);
            tmp = instance.load(std::memory_order_relaxed);
            if (tmp == nullptr) {
                tmp = new Singleton();
                almost_done.store(true, std::memory_order_relaxed); // 1
                std::atomic_thread_fence(std::memory_order_release);
                instance.store(tmp, std::memory_order_relaxed); // 2
            }
        }
        return tmp;
    }
    

    如果可以,为什么?理由是什么?

    我知道没有什么可以“走出”一个获取发布部分,但不能 2 输入并重新排序 1 是吗?

    我知道我不需要C++中的线程安全单体这样复杂的技术,是的,没有什么意义。 差不多完成了 ,这纯粹是为了学习。

    1 回复  |  直到 7 年前
        1
  •  2
  •   LWimsey    7 年前

    您的代码显示了双重检查锁定模式(DCLP)的有效实现。
    同步由 std::mutex std::atomic::instance 取决于线程输入代码的顺序。

    其他线程是否可以将实例视为不为空,但几乎完成仍为false?

    不,这是不可能的。

    dclp模式保证在开始时执行加载获取(返回非空值)的所有线程都能看到 instance 指向有效内存和 almost_done==true 因为加载已与存储版本同步。

    人们可能认为这是可能的一个原因,是在机会的小窗口中,第一根线(1)抓住了 std::互斥 当第二个线程(2)进入第一个线程时 if -陈述。

    在2号锁之前 std::互斥 ,它可以观察到 实例 (仍然指向未同步的内存,因为互斥锁对此负责,但尚未同步)。
    但即使发生了这种情况(这种模式中的一个有效场景),2将看到 几乎完成=真 因为释放围栏(由1所称)命令商店放松 almost_done 在商店放松之前 实例 其他线程也遵循同样的顺序。