为双重检查锁定提供适当的编译器内在特性
Proper compiler intrinsics for double-checked locking?
当实现双重检查锁定时,在初始化时实现双重检查锁定的内存和/或编译器屏障的正确方法是什么?
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;
}
在弱顺序内存体系结构上,我的理解是,在写入MyClass::Create
或MyClass::MyClass
的其他变量可见之前,s_object
的新值可能对其他线程可见。此外,编译器本身可以在没有编译器屏障的情况下以这种方式排列代码(在Visual c++中,_WriteBarrier
,但_InterlockedCompareExchange
充当屏障)。
我是否需要像一个存储栅栏内在函数在那里或一些东西,以确保MyClass
的变量是可见的所有线程之前,s_object
成为-1
以外的东西?
幸运的是,c++中的规则非常简单:
如果存在数据争用,则行为未定义
在您的代码中,数据竞争是由以下读操作引起的,它与__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++ 11实现,任何函数局部static
变量都将由通过该变量的第一个线程以线程安全的方式构造。
- 为什么编译器不检查被覆盖函数的存储类?
- 检查工作正常的 CXX 编译器:/cygdrive/c/cygwin64/bin/clang++ -- 已损坏
- 编译器标志在 CMake CHECK_CXX_COMPILER_FLAG 中检查了两次
- C++不给出越界错误.有没有办法强制编译器检查而不是未定义的行为?
- 如果检查和内联条件之间是否存在编译器差异
- C 编译器未检查模板类中是否存在一种方法
- 如何检查编译器错误是否在CMD中显示
- 我正在尝试检查可接受的身高和体重,并让 c++ 编译器输出候选人被拒绝的原因
- C++使用模板来避免编译器检查布尔值
- 编译器/JIT 优化 Java 和 C++ 中 for 循环的边界检查
- 为什么shared_ptr的这个向量定义可以通过编译器检查
- 如何检查如果MS编译器将编译我的源代码
- 在线编译器工具是否执行所有操作,或者它们只是检查是否编译
- 检查c++编译器隐式生成的代码
- 检查C++编译器是否存在
- 当使用scons构建时,无法使用IAR臂编译器进行编译.License检查失败
- 为双重检查锁定提供适当的编译器内在特性
- 如何更改/检查b2在编译Boost时使用的编译器
- 检查CMake如何标记不同的c++编译器
- 如何部分禁用cmake C/C++自定义编译器检查