是什么使单例线程不安全
What makes a singleton thread-unsafe?
我在某处读到单例是线程不安全的。我试图理解为什么会这样。如果我有一个这样的单例对象:
class singleton final
{
public:
static singleton& instance()
{
static singleton unique;
return unique;
}
private:
singleton() = default;
singleton(singleton const&) = delete;
singleton& operator=(singleton const&) = delete;
};
如果我有这样的代码:
singleton *p1, *p2;
auto t1 = std::thread([] { p1 = &singleton::instance(); });
auto t2 = std::thread([] { p2 = &singleton::instance(); });
t1.join();
t2.join();
p1
和 p2
是否可以指向两个不同的singleton
实例?如果unique
是static
,它的"静态"性质在完全初始化之前不会生效吗?如果是这样,这是否意味着可以并发访问静态对象的初始化,从而允许创建多个静态对象?
在 C++98/03 中,一个文件本地静态:
X& instance()
{
static X x;
return x;
}
意味着您的代码将执行以下操作:
bool __instance_initialized = false;
alignas(X) char __buf_instance[sizeof(X)];
// ...
X& instance()
{
if (!__instance_initialized)
{
::new(__buf_instance) X;
__instance_initialized = true;
}
return *static_cast<X*>(__buf_instance);
}
其中提供了以"__"为前缀的名称编译器。
但是在上面的代码中,没有什么能阻止两个线程同时进入if
,并且都试图同时构造X
。 编译器可能会尝试通过编写以下内容来解决此问题:
bool __instance_initialized = false;
alignas(X) char __buf_instance[sizeof(X)];
// ...
X& instance()
{
if (!__instance_initialized)
{
__instance_initialized = true;
::new(__buf_instance) X;
}
return *static_cast<X*>(__buf_instance);
}
但是现在一个线程可以将__instance_initialized
设置为 true 并开始构造X
,并在第一个线程仍在忙于构造X
时进行第二个线程测试并跳过if
。 然后,第二个线程将向其客户端提供未初始化的内存,直到第一个线程最终完成构造。
在 C++11 中,语言规则发生了变化,编译器必须设置代码,使第二个线程不能运行过去,也不能在第一个线程成功完成构造之前开始构造X
。 这可能意味着第二个线程必须等待任意时间才能继续......直到第一个线程完成。 如果第一个线程在尝试构造X
时抛出异常,则第二个线程将唤醒并尝试构造它。
以下是 Itanium ABI 规范,用于说明编译器如何实现这一点。
相关文章:
- 为什么我的 std::atomic<int> 变量不是线程安全的?
- 在不减慢线程速度的情况下保存大量数据
- OMP 不启动线程
- gdb:在多线程程序中调用函数,不进行线程
- 为什么消息框不阻止线程?
- 有没有更好的方法可以使此代码线程安全?线程局部静态似乎是一个生硬的工具
- 如何在不冻结线程/应用程序的情况下减慢方法执行速度
- 为什么它不是线程安全以及如何获得线程安全?
- 为什么链表不是线程安全的?
- 为什么std::queue不是线程安全的
- Boost::类中的Mutex不是线程安全的
- c ++ 为什么我的日期解析不是线程安全的
- 为什么不从线程调用重载成员
- 我们如何在不杀死线程的情况下重新初始化线程
- 当gemm做时,sgemm不多线程
- c++不能理解为什么这段代码不是线程安全的
- Boost ptime在MinGW下不是线程安全的
- 为什么将条件写转换为无条件写不是线程安全的优化?
- 是否有任何cpp函数或对象(不包括从c继承的)不是线程安全的,即使每个线程对自己的数据进行操作
- 本地静态指针变量不是线程安全的