在以下C ++代码中线程安全实现DCL(双重检查锁定)吗? [英] Is the DCL(double-checked locking) implemented in the following C ++ code thread-safe?

查看:52
本文介绍了在以下C ++代码中线程安全实现DCL(双重检查锁定)吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一段由C ++中的"acquire-release"语义实现的DCL(双重检查锁定)代码.代码如下:

Here is a piece of code that is DCL (double-checked locking) implemented by ‘acquire-release’ semantics in C++. The code is as follows:

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的解释是: 具有此内存顺序的存储操作执行释放操作:在此存储之后,无法对当前线程中的任何读取或写入进行重新排序.当前线程中的所有写操作在获得相同原子变量的其他线程中都是可见的.

On https://en.cppreference.com/w/cpp/atomic/memory_order, the interpretation of memory_order_release is: A store operation with this memory order performs the release operation: no reads or writes in the current thread can be reordered after this store. All writes in the current thread are visible in other threads that acquire the same atomic variable.

我的理解是:加载存储,存储存储不能重新排序,但是没有说其他存储不能重新排序.

My understanding is: load-store, store-store can not be reordered, but did not say that other-store can not be reordered.

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

So I think: '1' includes not only read and write instructions, but also call instruction, then the call instruction may be reordered behind '2'; then '3' may get an unsafe 'tmp' pointer.

让我再次描述以上段落:

Let me describe the above paragraph again:

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

我认为"4"可能会在"2"之后重新排序.一个线程执行"2"后,另一个线程完成"3"并获得tmp指针.目前,tmp指针是不安全的Singleton指针.

I think ‘4’ may be reordered after ‘2’. After one thread executes ‘2’, then another thread completes ‘3’ and obtains the tmp pointer. At this time, the tmp pointer is an unsafe Singleton pointer.

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

So I have this question: Is the above code thread-safe?

推荐答案

是的,这很安全!

如果acquire-load返回null(即单例尚未初始化),则获取互斥量.互斥锁内部的重载可以放宽,因为m_instance的修改仍然受到互斥锁的保护,也就是说,如果其他线程已经初始化了单例,那么该线程的互斥锁释​​放必须在我们获取互斥锁之前发生操作,因此可以确保我们看到更新后的m_instance.

If the acquire-load returns null (i.e., the singleton is not yet initialized) you acquire the mutex. Inside the mutex the reload can be relaxed since the modifications of m_instance is protected by the mutex anyway, i.e., if some other thread has already initialized the singleton, then the mutex-release of that thread must have happend before our mutex-acquire operation, so it is guaranteed that we see the updated m_instance.

如果acquire-load(1)看到"由发布存储区(2)写入的值,则这两个操作彼此同步,从而建立了事前发生的关系,因此您可以安全地访问对象tmp指向.

If the acquire-load (1) "sees" the value written by the release-store (2), the two operations synchronize-with each other thereby establishing a happens-before relation, so you can safely access the object tmp points to.

更新
释放存储区也受互斥锁保护,并且tmp初始化的一部分不可能与存储区重新排序,这是不可能.通常,应该避免争论可能的重新排序.该标准对操作是否/如何重新排序没有任何规定.相反,它定义了(线程间)-事件前-关系.编译器可能执行的任何重新排序仅是应用事前发生关系的规则的结果.

Update
The release-store is also protected by the mutex, and it is not possible that a part of the initialization of tmp is reordered with the store. In general one should avoid to argue about possible reorderings. The standard says nothing about if/how operations can be reordered. Instead, it defines the (inter-thread)-happens-before relation. Any reorderings that a compiler may perform are merely a result of applying the rules of the happens-before relations.

如果获取负载(1)加载由释放存储区(2)写入的值,则两个操作彼此同步,从而建立事前发生关系,即(2)事前发生(3) ).但是,由于(1)在(2)之前被排序,并且事前-发生关系是传递的,因此必须保证(1)在(3)之前发生.因此,不可能用(2)对(1)(或它的一部分)进行重新排序.

If the acquire-load (1) loads the value written by the release-store (2), the two operations synchronize-with each other thereby establishing a happens-before relation, i.e., (2) happens-before (3). But since (1) is sequenced-before (2) and the happens-before relation is transitive, it has to be guaranteed that (1) happens-before (3). Thus it is not possible to reorder (1) (or parts of it) with (2).

这篇关于在以下C ++代码中线程安全实现DCL(双重检查锁定)吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆