以下 C++ 代码中实现的 DCL(双重检查锁定)是不是是线程安全的?

     2023-02-17     55

关键词:

【中文标题】以下 C++ 代码中实现的 DCL(双重检查锁定)是不是是线程安全的?【英文标题】:Is the DCL(double-checked locking) implemented in the following C ++ code thread-safe?以下 C++ 代码中实现的 DCL(双重检查锁定)是否是线程安全的? 【发布时间】:2020-05-08 12:57:06 【问题描述】:

这是一段 DCL(双重检查锁定)代码,由 C++ 中的“获取-释放”语义实现。代码如下:

std :: atomic <Singleton *> Singleton :: m_instance;
std :: mutex Singleton :: m_mutex;

Singleton * Singleton :: getInstance () 
    Singleton * tmp = m_instance.load (std :: memory_order_acquire); // 3
    if (tmp == nullptr) 
        std :: lock_guard <std :: mutex> lock (m_mutex);
        tmp = m_instance.load (std :: memory_order_relaxed);
        if (tmp == nullptr) 
            tmp = new Singleton; // 1
            m_instance.store (tmp, std :: memory_order_release); // 2
        
    
    return tmp;

在https://en.cppreference.com/w/cpp/atomic/memory_order上,memory_order_release的解释是: 具有此内存顺序的存储操作执行释放操作:在此存储之后,当前线程中的任何读取或写入都不能重新排序。当前线程中的所有写入在获取相同原子变量的其他线程中都是可见的。

我的理解是:load-store,store-store不能重排,但没说other-store不能重排。

所以我认为:'1'不仅包括读写指令,还包括调用指令,那么调用指令可能在'2'后面重新排序;那么 '3' 可能会得到一个不安全的 'tmp' 指针。

让我再描述一下上面的段落:

Disassemble ‘1’ into the following two possible pseudo-instructions:
tmp = allocate ();
call Singleton constructor (tmp); // 4

我认为“4”可能会在“2”之后重新排序。一个线程执行‘2’后,另一个线程完成‘3’并获得tmp指针。此时tmp指针是一个不安全的Singleton指针。

所以我有一个问题:上面的代码是线程安全的吗?

【问题讨论】:

FWIW,你可以通过Singleton&amp; Singleton :: getInstance () static Singleton instance; return instance; 获得所有这些内容 @NathanOliver 没错,c++11 保证了静态局部变量的线程安全。但我想知道这段代码是否线程安全? 【参考方案1】:

是的,很安全!

如果获取加载返回 null(即,单例尚未初始化),则您获取互斥锁。在互斥体内部,重新加载可以放松,因为m_instance 的修改无论如何都受互斥体保护,即,如果其他线程已经初始化了单例,那么该线程的互斥体释放必须在我们的互斥体获取之前发生操作,所以保证我们看到更新的m_instance

如果acquire-load(1)“看到”了release-store(2)写入的值,这两个操作会相互同步,从而建立happens-before关系,所以你可以安全地访问对象tmp指向。

更新 release-store 也受到互斥锁的保护,并且 不可能 tmp 的初始化部分与 store 重新排序。一般来说,应该避免争论可能的重新排序。该标准没有说明是否/如何重新排序操作。相反,它定义了(线程间)-happens-before 关系。编译器可能执行的任何重新排序仅仅是应用发生前关系规则的结果。

如果acquire-load(1)加载release-store(2)写入的值,这两个操作会同步,从而建立happens-before关系,即(2)happens-before(3 )。但是由于 (1) 是先于 (2) 排序的,并且发生之前的关系是传递的,因此必须保证 (1) 发生在 (3) 之前。因此,不可能用 (2) 重新排序 (1)(或其中的一部分)。

【讨论】:

“那个线程的互斥释放一定是在我们的互斥获取操作之前发生的”,没错。但是调用指令可能在'2'后面重新排序,所以,我们得到一个不安全的来自“3”的“tmp”指针。 对不起,我不太明白你的意思? release-store (2) 也受到互斥锁的保护。 我的表达不太好,我重新编辑了问题,你可以再看一遍。 我已经用更多细节更新了我的答案 - 我现在很清楚为什么你的代码是安全的。 非常感谢,我们应该使用 Happens-before 规则编程。

pythonpython中实现的单例,双重判断(代码片段)

查看详情

双重检查和锁定模式是不是适用于 C++(不是 11)?

】双重检查和锁定模式是不是适用于C++(不是11)?【英文标题】:Doesdoublecheckandlockpatternworksinc++(not11)?双重检查和锁定模式是否适用于C++(不是11)?【发布时间】:2014-02-1021:58:36【问题描述】:一方面我有这篇文章,写于2004... 查看详情

为啥在双重检查锁定中使用 Volatile.Write?

...latile.Write?【发布时间】:2021-10-2513:25:52【问题描述】:以下是C#书籍中的一些代码,展示了如何在多线程中构造单例模式:internalsealedclassSingleton//s_lockisrequiredforthr 查看详情

糟糕的双重检查加锁(dcl)

...巧很好,但是也有一些技巧存在一些缺陷,下面要结束的双重检查加锁(DCL)就是有缺陷的一类。  由于早期的JVM在性能上存在一些有待优化的地方,因此在并发编程中,延迟初始化经常被用来降低程序的开销。编写正确的延... 查看详情

双重检查锁定原理详解(代码片段)

...问题。比如,下面是非线程安全的延迟初始化对象的示例代码:publicclassUnsafeLazyInitializationprivatestaticInstance 查看详情

双重检查锁定

看"java并发编程的艺术"第3.8双重检查锁定与延迟初始化  在Java多线程程序中,有时候需要采用延迟初始化来降低初始化类和创建对象的开销。双重检查锁定是常见的延迟初始化技术,但它是一个错误的用法。本文将分析双重... 查看详情

双重检查锁定模式 - 在传递给 call_once 的 lambda 中捕获

...看HerbSutter的CppCon2014谈论无锁编程。在handoutpage7,我们有以下代码:staticunique_ptr<widget>widget: 查看详情

双重检查锁定与延迟优化(代码片段)

...。比如,下面是非线程安全的延迟初始化对象的示例代码。publicclassUnsafeLazyInitializationprivatestaticInstanceinstance;publicstaticInstancegetInstance()if(instance==null)//1:A线程执行instance=newInstance();//2:B线程执行returninstance;在... 查看详情

为啥在双重检查锁定中使用volatile

】为啥在双重检查锁定中使用volatile【英文标题】:Whyisvolatileusedindoublecheckedlocking为什么在双重检查锁定中使用volatile【发布时间】:2011-12-1222:21:18【问题描述】:从HeadFirst设计模式一书中,具有双重检查锁定的单例模式已实现... 查看详情

多线程中的双重检查锁

...定方法publicsynchorizedvoidtest()publicvoidtest()synchorized()//锁定代码块  作用:保证同一时刻只有一个线程执行synchorized锁定的方法   单例模式中有一种实现方式叫做双重检查锁,主要是为了更好,更安全的实现单例功能。先来... 查看详情

双重检查锁定的正确编译器内在函数?

】双重检查锁定的正确编译器内在函数?【英文标题】:Propercompilerintrinsicsfordouble-checkedlocking?【发布时间】:2014-06-0807:59:41【问题描述】:在实现双重检查锁定时,在为初始化实施双重检查锁定时,执行内存和/或编译器屏障的... 查看详情

双重检查锁定与延迟初始化

...问题。比如,下面是非线程安全的延迟初始化对象的示例代码:publicclassUnsafeLazyInitialization{privatestaticInstanceinstance;publicsta 查看详情

在 SQL 中使用双重检查锁定死锁

】在SQL中使用双重检查锁定死锁【英文标题】:Deadlockusingdouble-checkedlockinginSQL【发布时间】:2013-11-2810:48:46【问题描述】:我的SQL语句遇到死锁,我想在其中选择一个ID(如果存在),否则插入然后选择它。正如here所建议的那样... 查看详情

双重检查锁定和单例模式

单例模式的实现一:懒汉式//懒汉式单例类.在第一次调用的时候实例化自己publicclassSingleton{privateSingleton(){}privatestaticSingletonsingle=null;//静态工厂方法publicstaticSingletongetInstance(){if(single==null){single=newSingleton();}returns 查看详情

项目中用的双重检查锁定是怎么回事(代码片段)

...类初始化的解决方案8.1、提前初始化8.1.1、提前初始化的代码实现8.1.2、验证类初始化时静态实例被初始化8.2、基于提前初始化改造的类的延迟初始化九、两种解决方案的对比前言开心一笑一、定义的Foo类@DatapublicclassFooprivateSt... 查看详情

公司发的小师妹夸我好棒,因为我告诉了她项目中用的双重检查锁定是怎么回事(代码片段)

...类初始化的解决方案8.1、提前初始化8.1.1、提前初始化的代码实现8.1.2、验 查看详情

公司发的小师妹夸我好棒,因为我告诉了她项目中用的双重检查锁定是怎么回事(代码片段)

...类初始化的解决方案8.1、提前初始化8.1.1、提前初始化的代码实现8.1.2、验 查看详情

“双重检查锁定被破坏”是仅限java的东西吗?

】“双重检查锁定被破坏”是仅限java的东西吗?【英文标题】:is"Double-CheckedLockingisBroken"ajava-onlything?【发布时间】:2011-08-2223:02:00【问题描述】:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html的页面说双重检查... 查看详情