我使用的是Visual Studio 2013,它没有"魔法静电"功能尚未实现,因此本地静态变量初始化还不是线程安全的。所以,而不是
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;
}
它似乎有效(至少它通过了测试),但我不确定它是否是线程安全的,因为这里我们有静态局部变量初始化的潜在问题{多个线程中的{1}}和p_foo
。使用flag
初始化原始指针和初始化nullptr
似乎比调用std::once_flag
的构造函数更无辜,但我想知道它是否真的安全。
那么,最后一段代码片段有什么问题吗?
答案 0 :(得分:1)
到目前为止,最稳定的单例对象初始化方法是schwartz_counter。无论全局对象的初始化顺序如何,std::cin
,cout
等的实现方式以及它们如何始终有效。
适用于所有版本的c ++。
https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter
答案 1 :(得分:0)
如果Foo& GetInstance()
只是同一个编译单元的一部分,则定义初始化顺序,因此它是线程安全的。
但是,如果不是这种情况并且多个编译单元正在引用那么初始化顺序将取决于对Foo& GetInstance()
的调用的顺序,并且如果涉及多个线程,那么顺序是未定义的,因此不是线程安全
值得检查:
答案 2 :(得分:0)
从线程安全的初始化角度来看,您的上一个代码段很好。
但是,目前尚不清楚如何在调用Foo
的线程中使用GetInstance
对象。
由于您要返回对非const对象的引用,我认为线程可能会修改Foo
对象。
请记住,您需要额外的同步(例如a mutex
)
如果Foo
对象由其构造函数完全初始化,并且调用GetInstance
的线程只读取对象,则没有问题,但我建议返回const Foo &