在这样的单例实现中可以对两个商店进行重新排序吗?

Can two stores be reordered in such singleton implementation?

在下面的单例'get'函数中,其他线程是否可以看到instance为not-null,但almost_done仍然为false? (假设 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;
}

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

我不知道什么可以 "get out" 获取发布部分,但不能 2 输入它并用 1 重新排序?

我知道在 C++ 中我不需要如此复杂的线程安全单例技术,是的,在 almost_done 中没有太大意义,这纯粹是为了学习。

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

can other threads see instance as not-null, but almost_done still false?

不,这是不可能的。

DCLP 模式保证所有线程在开始时执行加载获取(returns 非空值),保证看到 instance 指向有效内存和 almost_done==true 因为加载已经和store-release同步了。

人们可能认为这是可能的一个原因是,第一个线程 (#1) 持有 std::mutex 而第二个线程 (#2) 持有 window 的机会很小输入第一个 if 语句。

在#2 锁定 std::mutex 之前,它可能会观察到 instance 的值(仍然指向未同步的内存,因为互斥体对此负责,但尚未同步)。
但即使发生这种情况(此模式中的有效场景),#2 也会看到 almost_done==true,因为释放栅栏(由 #1 调用)命令存储松弛到 almost_done 在 store-relaxed 到 instance 之前,其他线程观察到相同的顺序。