Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/132.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/6/multithreading/4.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++_Multithreading_Singleton_Double Checked Locking - Fatal编程技术网

C++ 这是否可以兑换线程安全的双重检查锁定模式?

C++ 这是否可以兑换线程安全的双重检查锁定模式?,c++,multithreading,singleton,double-checked-locking,C++,Multithreading,Singleton,Double Checked Locking,原始模式的问题已被详细记录:。我经常看到这个话题在网上被问到 我已经提出了一个版本,在我看来,它似乎解决了原始模式的种族条件问题,但在你看来它还可以吗 在下面的代码中,我假设LOCK是一种正确实现的互斥类型,它在LOCK/unlock处会导致内存障碍,我不会尝试处理实例的释放,因为这不是模式的一部分 我知道其他可能的单身方式,但这不是我想要的。我特别想问的是这个模式——竞争条件是通过在互斥锁之外分配来解决的吗 template <typename T, typename LOCK>

原始模式的问题已被详细记录:。我经常看到这个话题在网上被问到

我已经提出了一个版本,在我看来,它似乎解决了原始模式的种族条件问题,但在你看来它还可以吗

在下面的代码中,我假设LOCK是一种正确实现的互斥类型,它在LOCK/unlock处会导致内存障碍,我不会尝试处理实例的释放,因为这不是模式的一部分

我知道其他可能的单身方式,但这不是我想要的。我特别想问的是这个模式——竞争条件是通过在互斥锁之外分配来解决的吗

template <typename T, typename LOCK>
class Singleton
{
private:
    static T * instance_;
    static LOCK mutex;

    // private - inaccessible
    Singleton();
    Singleton(const Singleton &);
    Singleton & operator=(const Singleton &);

public:
    static T * get_instance()
    {
        if (instance_ == NULL)
        {
            T * temp = new T;
            mutex.lock();
            if (instance_ == NULL)
                instance = temp;
            else
                delete temp;
            mutex.unlock();
        }
        return instance_;
    }
};

不,这不安全。实例u上存在争用条件。如果实例u==NULL,而另一个线程正在向其写入instance=temp;,则一个线程可能正在从中读取;,因此,此代码具有未定义的行为


您正在询问由锁创建的单个内存围栏是否足够。从C++11的角度来看,情况并非如此。从非C++11的角度来看,我不能肯定,但在C++11之前的世界中,依赖非原子和非互斥类型进行同步似乎不太可能工作,互斥和原子变量只能通过编译器和处理器特定的黑客来工作,而且,依靠他们来做任何事情,而不仅仅是他们的简单规范,这似乎是愚蠢的。

不,这是不安全的。实例u上存在争用条件。如果实例u==NULL,而另一个线程正在向其写入instance=temp;,则一个线程可能正在从中读取;,因此,此代码具有未定义的行为


您正在询问由锁创建的单个内存围栏是否足够。从C++11的角度来看,情况并非如此。从非C++11的角度来看,我不能肯定,但在C++11之前的世界中,依赖非原子和非互斥类型进行同步似乎不太可能工作,互斥和原子变量只能通过编译器和处理器特定的黑客来工作,而且,依靠它们来做任何事情,而不仅仅是简单的规范,这似乎是愚蠢的。

正如在其他地方提到的,问题在于,在访问实例_uu1时存在数据竞争:第一个if语句读取值,而后一个对实例_uu的赋值写入该变量。如果这两个操作之间没有同步,则行为是未定义的。但是如果你有C++11,有一个简单的解决方案。更改声明

static T * instance_;


现在,instance_uu的所有访问都是原子的,这保证了不会撕裂并提供了同步。

正如其他地方提到的,问题在于,对instance_uuu的访问存在数据竞争:第一个if语句读取值,而对instance_uu的后续赋值写入该变量。如果这两个操作之间没有同步,则行为是未定义的。但是如果你有C++11,有一个简单的解决方案。更改声明

static T * instance_;


现在所有对实例的访问都是原子的,这保证了不撕裂并提供了同步。

只是在临时分配实例,发出一个内存屏障,然后将其填充到最终的共享变量中,然后解锁,这有什么不对?@KevinBallard,没有可移植的方法来实现内存屏障,阿法奎德问题。在C++11中有一种可移植的方法来实现内存屏障。在此之前,没有,但也没有创建互斥的可移植方法。还是一根线matter@jalf好的,我明白你的意思。所以这可以在c++11中用内存屏障重写,但这完全等同于这个版本。问题是一样的。只是在临时内存中分配实例,发出一个内存屏障,然后将其填充到最终的共享变量中,然后解锁,这有什么错?@KevinBallard,没有可移植的方法来实现内存屏障,AFAIKWeird问题。在C++11中有一种可移植的方法来实现内存屏障。在此之前,没有,但也没有创建互斥的可移植方法。还是一根线matter@jalf好的,我明白你的意思。所以这可以在c++11中用内存屏障重写,但这完全等同于这个版本。问题是一样的,我不确定我是否明白。你是说写指针不是原子的吗?如果写入线程在寄存器中具有temp后,但在写入线程之前被抢占,则读取线程将看到NULL并继续并命中互斥锁。@LuboAntonov:除非您显式创建指针,否则任何并发访问/修改都将导致未定义的行为。您认为在系统级别上,从寄存器到内存的写入不是原子的?在我看来,读取线程可能会看到0,在这种情况下,它将继续并停止在互斥锁上,或者看到正确的指针值,例如。没有中间可能性,内存位置只有h
alf地址或类似的东西。对于单例实现,您的版本很好,但每次访问实例时都会受到缓存同步的性能损失,基本上与volatile相同,对吗?。另外,这只在C++ 0x11中工作。正如我所提到的,我对DCL模式及其实现中可能出现的问题特别感兴趣。@MSalters,缓存没有问题-互斥操作有一个隐含的内存限制,或者你可以做一个显式的内存限制。请参阅问题的注释。问题是,标准没有定义低级内存存储操作的原子性。因此,在x86上,原子性得到了有效保证,因为没有编译器会将32位存储分解为2个16位存储。但在嵌入式体系结构上,可以想象一个32位地址存储在两条16位的机器指令中,如果线程被抢占,就会产生一个部分地址。我不确定我是否遵循了。你是说写指针不是原子的吗?如果写入线程在寄存器中具有temp后,但在写入线程之前被抢占,则读取线程将看到NULL并继续并命中互斥锁。@LuboAntonov:除非您显式创建指针,否则任何并发访问/修改都将导致未定义的行为。您认为在系统级别上,从寄存器到内存的写入不是原子的?在我看来,读取线程可能会看到0,在这种情况下,它将继续并停止在互斥锁上,或者看到正确的指针值,例如。没有中间可能性,内存位置只有地址的一半或类似的东西。就单例实现而言,您的版本很好,但每次访问实例时都会遭受缓存同步的性能损失,基本上与volatile right?相同?。另外,这只在C++ 0x11中工作。正如我所提到的,我对DCL模式及其实现中可能出现的问题特别感兴趣。@MSalters,缓存没有问题-互斥操作有一个隐含的内存限制,或者你可以做一个显式的内存限制。请参阅问题的注释。问题是,标准没有定义低级内存存储操作的原子性。因此,在x86上,原子性得到了有效保证,因为没有编译器会将32位存储分解为2个16位存储。但在嵌入式体系结构上,可以想象一个32位的地址存储在两条16位的机器指令中,如果线程被抢占,就会产生一个部分地址。