Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/149.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/maven/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在这种单例实现中,两个存储可以重新排序吗?_C++_C++14_Atomic_Memory Barriers - Fatal编程技术网

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

C++ 在这种单例实现中,两个存储可以重新排序吗?,c++,c++14,atomic,memory-barriers,C++,C++14,Atomic,Memory Barriers,在下面的单例“get”函数中,其他线程是否可以将实例视为非null,但几乎完成仍然为false?(假设几乎完成最初是错误) Singleton*Singleton::Get(){ auto tmp=instance.load(std::memory\u order\u released); std::原子线程围栏(std::内存顺序获取); 如果(tmp==nullptr){ std::锁紧防护装置(锁); tmp=instance.load(std::memory\u order\u rele

在下面的单例“get”函数中,其他线程是否可以将
实例
视为非null,但
几乎完成
仍然为false?(假设
几乎完成
最初是
错误

Singleton*Singleton::Get(){
auto tmp=instance.load(std::memory\u order\u released);
std::原子线程围栏(std::内存顺序获取);
如果(tmp==nullptr){
std::锁紧防护装置(锁);
tmp=instance.load(std::memory\u order\u released);
如果(tmp==nullptr){
tmp=新单例();
差不多完成了。存储(true,std::memory\u order\u released);//1
std::原子线程围栏(std::内存命令释放);
store(tmp,std::memory_order_released);//2
}
}
返回tmp;
}
如果可以,为什么?理由是什么

我知道没有任何东西可以“退出”acquire发布部分,但是不能
2
进入它并用
1
重新排序


我知道我不需要C++中的线程安全单体这样复杂的技术,是的,在AlMOSTSTOW/<代码>中没有太多的意义,这纯粹是为了学习。< /P> < P>代码显示了双检查锁定模式(DCLP)的一个有效实现。 同步由

std::mutex
std::atomic::instance
处理,具体取决于线程输入代码的顺序

其他线程是否可以将实例视为NOTNULL,但仍为false

不,这是不可能的

DCLP模式保证在开始时执行load acquire(返回非空值)的所有线程都能在有效内存中看到
实例
点,并且
几乎完成==true
因为加载已与存储版本同步

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

在#2锁定
std::mutex
之前,它可能会观察到
实例
的值(仍然指向未同步的内存,因为mutex负责,但尚未同步)。
但即使发生了这种情况(在这种模式中是一种有效的场景),#2也会看到
几乎完成==true
,因为发布围栏(由#1调用)命令商店放松到
几乎完成

在存储放松到
实例之前,其他线程遵循相同的顺序。

为什么不移动此行std::lock\u guard guard(lock);作为函数的第一行,这不会解决任何同步吗issues@OmidCompSCI正如我所说的,这纯粹是为了学习,但这只是标准的双重检查锁定,带有外部if,用于减少实例创建后的争用initialized@ledonter返回语句是missing@iMajuscule我认为“标准::原子线程围栏(标准::内存顺序获取);“应该写在”if(tmp==nullptr){”之后:当tmp不是nullptr时,我想我们不想这样做?(我知道这只是为了学习,但我认为这会更有意义)我问的不是其他线程调用相同的函数,我问的是一个线程#2只是对
几乎完成的
实例
进行了松弛加载,没有任何获取操作。不管怎样,为什么释放栅栏会阻止存储被重新排序?我在标准中没有发现类似的情况(坦白说,任何地方都可以)。如果你说“按定义”,请提供它,因为标准中显然不存在它:(@ledonter A release fence阻止所有先前的内存操作在后续写入之后重新排序。请查看Jeff Preshing关于此主题的文章(或)这正是我要问的!我读了那篇文章,是的,它是按照你引用的那样说的,还有一些其他的文章也是。但是很多人都在说,实际上任何东西(读和写)可以从底部进入获取发布部分。这是我困惑的根源。它是如何定义的C++?或者它不是由C++定义的,只是由架构定义的?更重要的是,允许任何东西进入并允许从顶部到底部的存储的原理是什么?它是这样的模式吗?他命令商店几乎完成(在线程1中)在存储到
实例之前
。这意味着线程2以相同的顺序观察两个存储。这并不意味着线程2可以使用该顺序,而无需设置获取围栏。原因是内存顺序是双向的;也就是说,线程2所做的任何事情都可以无序观察R.通过线程1来保证一个有效的线程间发生在关系之前,必须使用与C++标准定义的关系同步。@ Leunter的问题是其他线程如何能看到变量,而不是如何使用它。这是一个非常微妙的差别。C++标准定义了与关系的同步(同时具有获取和释放),因为C++程序需要可靠地访问线程之间共享的数据。
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;
}