tem*_*boy 1 c++ singleton multithreading thread-safety c++11
我在某处读到单例是线程不安全的.我试图理解为什么会这样.如果我有一个像这样的单例对象:
class singleton final
{
public:
static singleton& instance()
{
static singleton unique;
return unique;
}
private:
singleton() = default;
singleton(singleton const&) = delete;
singleton& operator=(singleton const&) = delete;
};
Run Code Online (Sandbox Code Playgroud)
如果我有这样的代码:
singleton *p1, *p2;
auto t1 = std::thread([] { p1 = &singleton::instance(); });
auto t2 = std::thread([] { p2 = &singleton::instance(); });
t1.join();
t2.join();
Run Code Online (Sandbox Code Playgroud)
是否有可能p1和p2指向两个不同的singleton实例?如果unique是static,它的"静态"性质是否在完全初始化之前不会生效?如果是这样,这是否意味着可以同时访问静态对象的初始化,从而允许创建多个静态对象?
How*_*ant 11
在C++ 98/03中一个文件本地静态:
X& instance()
{
static X x;
return x;
}
Run Code Online (Sandbox Code Playgroud)
意味着你的代码会做这样的事情:
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);
}
Run Code Online (Sandbox Code Playgroud)
"__" - 带前缀的名称是编译器提供的.
但是在上面的代码中,没有任何东西阻止两个线程同时进入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);
}
Run Code Online (Sandbox Code Playgroud)
但是现在有可能将一个线程设置__instance_initialized为true并开始构造X,并进行第二个线程测试并跳过if第一个线程仍忙于构建的时间X.然后,第二个线程将向其客户端提供未初始化的内存,直到第一个线程最终完成构造.
在C++ 11中,语言规则被更改,以便编译器必须设置代码,使得第二个线程无法运行,也不会X在第一个线程成功完成构造之前开始构造.这可能意味着第二个线程必须等待任意时间才能继续...直到第一个线程完成.如果第一个线程在尝试构造时抛出异常X,则第二个线程将唤醒并尝试构建它.
以下是关于编译器如何实现该目标的Itanium ABI规范.