C++11中的局部静态变量初始化线程安全吗? [复制]

Posted

技术标签:

【中文标题】C++11中的局部静态变量初始化线程安全吗? [复制]【英文标题】:Is local static variable initialization thread-safe in C++11? [duplicate] 【发布时间】:2011-12-27 11:27:25 【问题描述】:

我知道这是一个经常被问到的问题,但由于有很多变体,我想重新陈述它,并希望有一个反映当前状态的答案。类似的东西

Logger& g_logger() 
    static Logger lg;
    return lg;

变量lg的构造函数是否保证只运行一次?

我从以前的答案中知道,在 C++03 中,这不是;在 C++0x 草案中,这是强制执行的。但我想要一个更清晰的答案

    在 C++11 标准(非草稿)中,线程安全的初始化行为是否最终确定? 如果以上是肯定的,在当前最新版本的流行编译器中,即 gcc 4.7、vc 2011 和 clang 3.0,它们是否正确实现?

【问题讨论】:

@Chris:确定性初始化和避免静态初始化顺序惨败。第一次调用函数时,将首先初始化局部静态变量。 感谢 Xeo,这是主要原因。其他一些包括: 1. 通常在日志系统中,客户端代码将其用作宏,例如 LOG @balki,GCC 已经实施了近十年。 Clang 也支持。 Visual Studio 2013 也不会出现。请参阅msdn.microsoft.com/en-us/library/vstudio/… 上的“Magic statics”行 “魔法静力学”终于在 VS 2015 中出现了:blogs.msdn.com/b/vcblog/archive/2014/11/17/… 【参考方案1】:

相关第6.7节:

这样的变量在控件第一次通过其声明时被初始化;这样的变量在其初始化完成时被认为已初始化。 [...] 如果在初始化变量时控件同时进入声明,则并发执行将等待初始化完成。

然后是一个脚注:

该实现不得在初始化程序的执行过程中引入任何死锁。

所以是的,你很安全。

(这当然没有说明后续通过引用访问变量。)

【讨论】:

重要的是要注意static Logger lg; 是线程安全的,只有当Logger 的默认构造函数是线程安全的,即它不会在内部访问任何可修改的共享资源,比如通过全局变量或单例。需要注意的是,标准保证only这一点:如果多个线程尝试同时开始执行构造函数,则只有其中一个线程会实际执行它,其余线程将等待用于完成初始化。然而,标准并没有对构造函数本身的线程安全提供任何保证。 @Nawaz:为什么构造函数必须是线程安全的?你自己说过只有一个线程会执行构造函数。 @KerrekSB:我还解释了我的意思:即它不会在内部访问任何可修改的共享资源。仅仅因为只有一个线程执行构造函数,并不一定意味着它所做的事情是线程安全的。如果它修改了不受保护的共享资源,那么它就不是线程安全的。 @Nawaz:嗯,确实如此,但这也是一个完整的概括:共享数据的并发访问必须同步。我不认为静态初始化会以某种方式提供对该规则的豁免,因此我认为不值得特别指出。 这对static Logger *lg = new Logger(); 也有效吗?【参考方案2】:

--fno-threadsafe-statics 也值得一提。在 gcc 中:

不要发出额外的代码来使用 C++ ABI 中指定的例程来进行局部静态的线程安全初始化。您可以使用此选项在不需要线程安全的代码中稍微减少代码大小。

另外,看看旧线程Are function static variables thread-safe in GCC?

【讨论】:

以上是关于C++11中的局部静态变量初始化线程安全吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

java 局部静态变量在多线程环境下是不是有线程安全问题??

局部静态的线程安全初始化:MSVC [重复]

C++11:在多线程程序中使用局部静态变量导致 coredump

java中的全局变量和静态变量是在编译时分配内存还是在加载时分配内存??

Java静态初始化器线程安全吗?

java线程安全问题之静态变量实例变量局部变量