Is local static variable initialization thread-safe in C++11?
我知道这是一个经常被问到的问题,但是由于有很多变种,我想重新陈述它,并希望有一个反映当前状态的答案。类似的东西
1 2 3 4
| Logger& g_logger() {
static Logger lg;
return lg;
} |
变量lg的构造函数是否保证只运行一次?
我从前面的答案中知道,在C++ 03中,这不是;在C++ 0x草稿中,这是强制的。但是我想要一个更清楚的答案
在C++ 11标准(未草稿)中,线程安全初始化行为是否已完成?
如果以上是肯定的,那么在当前流行的编译器的最新版本中,即GCC4.7、VC 2011和Clang3.0,它们是否得到了正确的实现?
- 我能问一下这个功能的用途吗?它与简单的全球Logger g_logger;相比有什么优势?
- @克里斯:确定性初始化和避免静态初始化顺序失败。第一次调用函数时,将首先初始化本地静态。
- 谢谢Xeo,这是主要原因。其他一些包括:1.通常在日志系统中,客户机代码将其用作宏,如log<"your log"…,并且宏必须具有对logger 2的确定性访问权。如果不使用记录器,则不会创建它。三.您可能不希望您的客户机创建多个记录器(存在同步问题等),因此记录器有一个私有的构造函数,该构造函数只能由朋友g_logger()访问)
- 对于第二个问题,还没有编译器实现这一点。
- @巴尔基,海湾合作委员会已经实施了近十年。Clang也支持它。
- Visual Studio 2012更新3不支持它-我用一个简短的测试程序测试了它,并查看了程序集代码。
- 它也不会出现在Visual Studio 2013中。请参阅msdn.microsoft.com/en-us/library/vstudio/&hellip;上的"magic statics"行。
- 如果在任何其他线程运行之前,确保从main()调用一次g_logger,那么在VS2013中是否安全?
- "魔法静力学"终于与vs 2015一起出现:blogs.msdn.com/b/vcblog/archive/2014/11/17/&hellip;
相关第6.7节:
such a variable is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. [...] If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.
还有一个脚注:
The implementation must not introduce any deadlock around execution of the initializer.
所以是的,你很安全。
(这当然没有说明通过引用对变量的后续访问。)
- open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm打开标准。
- 需要注意的是,只有当Logger的默认构造函数为线程安全时,static Logger lg;才是线程安全的,即它不通过全局变量或单例在内部访问任何可修改的共享资源。应该注意的是,标准只保证这样做:如果有多个线程试图同时开始执行构造函数,那么只有一个线程会实际执行它,其余的线程将等待初始化完成。然而,该标准并不能保证构造函数本身的线程安全。
- 另外,它没有提到变量的读卡器,这意味着如果有任何读卡器前面没有静态构造函数,那么就可能存在TSAN问题。当然,如果您使用上面的模式(Foo&App:GoFooCoor){static Fo.bar;返回bar;}则不会出现TSAN问题。
- 纳瓦兹:为什么构造函数必须是线程安全的?您自己说只有一个线程将执行构造函数。
- @Kerreksb:我还解释了我的意思:也就是说,它不会在内部访问任何可修改的共享资源。仅仅因为只有一个线程执行一个构造函数,并不一定意味着它所做的是线程安全的。如果它修改了不受保护的共享资源,那么它就不会是线程安全的。
- @纳瓦兹:没错,但这也是一个完全的普遍性:共享数据的并发访问必须同步。我不认为有任何建议静态初始化会以某种方式提供对该规则的豁免,所以我认为不值得具体地调用它。
- 这个问题和答案都没有提到"迈尔斯·辛格尔顿",这似乎有点丢脸。
- 这对static Logger *lg = new Logger();也有效吗?
- @用户3819404:在块范围内,是的。(但对象将永远不会被删除。这可能是可取的。)
--FNO螺纹安全静力学也值得一提。在海湾合作委员会:
Do not emit the extra code to use the routines specified in the C++ ABI for thread-safe initialization of local statics. You can use this option to reduce code size slightly in code that doesn't need to be thread-safe.
还有,看看老线程是GCC中线程安全的函数静态变量吗?