gpt4 book ai didi

c++ - 用于双重检查锁定的正确编译器内在函数?

转载 作者:塔克拉玛干 更新时间:2023-11-03 06:44:06 25 4
gpt4 key购买 nike

在实现双重检查锁定时,在为初始化实现双重检查锁定时,内存和/或编译器屏障的正确方法是什么?

像 std::call_once 这样的东西不是我想要的;它太慢了。它通常只是在操作系统的 pthread_mutex_lock 和 EnterCriticalSection 之上实现。

在我的程序中,我经常遇到初始化情况,在这种情况下,只要有一个线程设置最终指针,就可以安全地重复初始化。如果另一个线程先于它设置指向单例对象的最终指针,它会删除它创建的内容并使用另一个线程的。我也经常在哪个线程“获胜”无关紧要的情况下使用它,因为它们都得出相同的结果。

这是一个使用 Visual C++ 内部函数的不安全、过度设计的示例:

MyClass *GetGlobalMyClass()
{
static MyClass *const UNSET_POINTER = reinterpret_cast<MyClass *>(
static_cast<intptr_t>(-1));

static MyClass *volatile s_object = UNSET_POINTER;

if (s_object == UNSET_POINTER)
{
MyClass *newObject = MyClass::Create();

if (_InterlockedCompareExchangePointer(&s_object, newObject,
UNSET_POINTER) != UNSET_POINTER)
{
// Another thread beat us. If Create didn't return null, destroy.
if (newObject)
{
newObject->Destroy(); // calls "delete this;", presumably
}
}
}

return s_object;
}

在弱序内存架构上,我的理解是 s_object 的新值可能对其他线程可见 之前 中写入的其他变量MyClass::CreateMyClass::MyClass 可见。此外,编译器本身可以在没有编译器屏障的情况下以这种方式安排代码(在 Visual C++ 中,_WriteBarrier,但 _InterlockedCompareExchange 充当屏障)。

s_object 变成除 -1?

最佳答案

幸运的是,C++ 中的规则非常简单:

If there is a data race, the behaviour is undefined.

在您的代码中,数据竞争是由以下读取引起的,它与 __InterlockedCompareExchangePointer 中的写入操作冲突。

if (s_object.m_void == UNSET_POINTER)

无阻塞的线程安全解决方案可能如下所示。请注意,在 x86 上,与常规加载操作相比,具有顺序一致性 的加载操作基本上没有开销。如果您关心其他架构,您也可以使用获取发布而不是顺序一致性

static std::atomic<MyClass*> s_object{nullptr};

MyClass* o = s_object.load(std::memory_order_seq_cst);
if (o == nullptr) {
o = new MyClass{...};
MyClass* expected = nullptr;
if (!s_object.compare_exchange_strong(expected, o, std::memory_order_seq_cst)) {
delete o;
o = expected;
}
}
return o;

关于c++ - 用于双重检查锁定的正确编译器内在函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24104140/

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