gpt4 book ai didi

c++ - C++11 中双重检查锁定模式 (DCLP) 的实现是否正确?

转载 作者:行者123 更新时间:2023-11-30 02:38:41 25 4
gpt4 key购买 nike

我正在阅读有关 DCLP(双重检查锁定模式)的信息,但我不确定自己是否理解正确。当使用原子创建锁时(如 DCLP fixed in C++11 中所述),有两件事不清楚:

  1. 文章中的代码:
std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance() {
Singleton* tmp = m_instance.load(std::memory_order_acquire);
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;
m_instance.store(tmp, std::memory_order_release);
}
}
return tmp;
}

如果我在“load()”中获取栅栏,但 tmp 不是 nullptr,我只是返回,会发生什么?我们不应该说明 CPU 可以在哪里“释放围栏”吗?

如果不需要释放围栏,那我们为什么要获取和释放?有什么区别?

很遗憾,我遗漏了一些基本的东西......

  1. 如果我没看错那篇文章,这是否也是实现 DCLP 的正确方法?
Singleton* Singleton::m_instance = null;
std::atomic<bool> Singleton::is_first; // init to false
std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance() {
bool tmp = is_first.load(std::memory_order_acquire);
if (tmp == false) {
std::lock_guard<std::mutex> lock(m_mutex);
tmp = is_first.load(std::memory_order_relaxed);
if (tmp == false) {
// can place any code that will run exactly once!
m_instance = new Singleton;

// store back the tmp atomically
is_first.store(tmp, std::memory_order_release);
}
}
return m_instance;
}

换句话说,我没有查看实例,而是使用原子 bool 值来确保 DCLP 正常工作,并且第二个 tmp 中的任何内容都将被同步并运行一次。是否正确?

谢谢!

编辑:请注意,我不是在问实现单例的问题,而只是为了更好地理解 fences 和 atomic 的概念以及它如何修复 DCLP。这是一个理论问题。

最佳答案

What happens if I aquire the fence inside "load()", but than tmp is not nullptr, and I simply return? Shouldn't we state where the CPU can "release the fence"?

没有。当商店进入 m_instance 时发布完成发生。如果加载 m_instance并且它不为空,那么发布已经发生在更早的时候,你不需要这样做。

您不会像获得互斥锁那样“获得栅栏”和“释放栅栏”。栅栏不是这样的。栅栏只是一个没有关联内存位置的获取或释放操作。栅栏在这里并不真正相关,因为所有获取和释放操作都有一个关联的内存位置(原子对象 m_instance )。

您不必像互斥锁+解锁那样成对地获取+释放。您可以执行一个释放操作来存储一个值,并进行任意数量的获取操作(零个或多个)来加载该值并观察其效果。

加载/存储上的获取/释放语义与加载/存储两侧的操作排序相关,以防止重新排序。

对变量 A 的非松弛原子存储(即释放操作)将同一变量 A 的稍后非松弛原子加载(即获取操作)同步

正如 C++ 标准所说:

Informally, performing a release operation on A forces prior side effects on other memory locations to become visible to other threads that later perform a consume or an acquire operation on A.

因此在您引用的 DCLP 代码中,m_instance.store(tmp, memory_order_release)m_instance的商店并且是释放操作。 m_instance.load(memory_order_acquire)是来自 m_instance 的负载并且是一个获取操作。内存模型表示,非空指针的存储与任何看到非空指针的加载同步,这意味着可以保证 new Singleton 的所有效果。在任何线程可以从 tmp 加载非空值之前已经完成.这修复了 C++11 之前的双重检查锁定存储到 tmp 的问题。在对象完全构造之前,其他线程可以看到它。

In other words, instead of looking at the instance I am using an atomic boolean to make sure the DCLP works, and whatever is inside the second tmp is surly to be syncronized and run once. Is it correct?

不,因为你存储了false这里:

        // store back the tmp atomically
is_first.store(tmp, std::memory_order_release);

这意味着在下次调用该函数时,您将创建另一个 Singleton并泄漏第一个。应该是:

        is_first.store(true, std::memory_order_release);

如果你解决了这个问题,我认为它是正确的,但在典型的实现中它使用更多的内存( sizeof(atomic<bool>)+sizeof(Singleton*) 可能比 sizeof(atomic<Singleton*>) 多),并且通过将逻辑分成两个变量(一个 bool 值和一个指针)你像你一样更容易犯错。因此,与原始方式相比,这样做没有任何优势,在原始方式中,指针本身也用作 bool 值,因为您直接查看指针,而不是某些可能未正确设置的 bool 值。

关于c++ - C++11 中双重检查锁定模式 (DCLP) 的实现是否正确?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30587666/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com