使用本地静态std :: once_flag和本地静态指针的线程安全初始化静态变量
我正在使用Visual Studio 2013,它尚未实现“魔术静态”功能,因此本地静态变量初始化不是但线程安全。所以,与其使用本地静态std :: once_flag和本地静态指针的线程安全初始化静态变量
Foo& GetInstance()
{
static Foo foo;
return foo;
}
我做这样的事情:
std::unique_ptr<Foo> gp_foo;
std::once_flag g_flag;
Foo& GetInstance()
{
std::call_once(g_flag, [](){ gp_foo = std::make_unique<Foo>(); });
return *gp_foo;
}
,但我不喜欢gp_foo
和g_flag
全局变量(第一,问题的静态初始化的秩序观念在不同的翻译单元变量;第二,我想初始化变量,只有当我们需要他们,第一次调用的GetInstance())后,即,所以我采取了以下:
Foo& GetInstance()
{
// I replaced a smart pointer
// with a raw one just to be more "safe"
// not sure such replacing is really needed
static Foo *p_foo = nullptr;
static std::once_flag flag;
std::call_once(flag, [](){ p_foo = new Foo; });
return *p_foo;
}
它似乎工作(至少它通过了测试),但我不确定它是线程安全的,因为在这里我们有与多个线程中的静态局部变量p_foo
和flag
的初始化相同的潜在问题。使用nullptr
初始化原始指针并初始化std::once_flag
似乎比调用Foo
的构造函数更无害,但我想知道它是否非常安全。
那么,最后一段代码有什么问题吗?
如果Foo& GetInstance()
只是同一编译单元的一部分,则定义初始化顺序,因此它是线程安全的。然而,如果上面不是这种情况,并且多个编译单元正在引用,那么初始化顺序将取决于调用Foo& GetInstance()
的调用顺序,并且如果涉及多个线程,则顺序未定义,因此不是线程安全的。
值得一看:
到现在为止,单对象初始化最稳定的方法是schwartz_counter。这就是如何实现std::cin
,cout
等以及它们如何始终工作,而不管全局对象的初始化顺序如何。
它适用于所有版本的C++。
https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter
顺便说一下,是否保证在加载时所有全局变量的初始化是从同一个线程执行的?如果我们有2个[Delay-Loaded DLLs](https://msdn.microsoft.com/en-us/library/151kt790.aspx),例如_lib1.dll_带有_func1_和_lib2.dll_函数_func2_,以及_func1_和_func2_从不同的线程同时被调用? – undermind
无论如何,它并没有真正使这个习语无用,因为我们可以安全地用'std :: atomic
@undermind schwartz计数的对象在未初始化的全局变量清零之后和main运行之前初始化。 在一个DLL中,它会在DllMain运行之前发生,我相信这发生在所有线程附件代码之前。但我必须检查,因为我很久没有使用DLL了。受不了他们! –
你最后的代码片断是从一个线程安全的初始化点罚款。
但是,您不清楚如何在调用GetInstance
的线程中使用Foo
对象。 由于您正在返回对非const对象的引用,因此我认为线程可能会修改Foo
对象。请记住,您需要为此添加其他同步(例如,一mutex
)
如果Foo
对象完全被它的构造和线程调用GetInstance
将只从对象读取初始化,是没有问题的,但我会建议返回const Foo &
HTTPS://codereview.stackexchange。 com/ – Blacktempel
请参阅[call_once on cppreference](http://en.cppreference.com/w/cpp/thread/call_once);特别是第2点。 – Simple
@Simple _在上述所选函数的执行成功完成之前,组中的所有调用都没有返回,也就是说,不会通过exception._退出,但我的担忧与std :: call_once,而是关于这两行:'static Foo * p_foo = nullptr; std :: once_flag标志;' – undermind