[英]initialization order of thread_local vs. global variables
查:
#include <iostream>
class C {
public:
explicit C(int id) { std::cout<<"Initialized "<<id<<"\n"; }
};
1.cpp:
#include "C.h"
C global(1);
2.cpp:
#include "C.h"
thread_local C thread(2);
int main() {}
我的问题是:是否保证global
将在thread
之前初始化?
C++ 标准在这一点上有些含糊,据我所知。 它说(来自 C++17 n4659草案):
[basic.start.static] 静态初始化
作为程序启动的结果,具有静态存储持续时间的变量被初始化。 作为线程执行的结果,具有线程存储持续时间的变量被初始化。
按理说,“程序启动”发生在“线程执行”之前,但由于这两个表达式仅出现在标准中的那个地方,我正在寻求实际语言律师的建议。
我将使用 C++20 工作草案,因为那里的措辞更简洁一些,尽管真正的规则没有改变。
首先,就非本地而言, thread_local
行为基本上类似于static
: [basic.stc.thread]/2 :
[注意:具有线程存储持续时间的变量按照 [basic.start.static]、[basic.start.dynamic] 和 [stmt.dcl] 中的规定进行初始化,如果构造了,则在线程退出时销毁 ([basic. start.term])。 — 尾注 ]
是的,是笔记。 但是声明为thread_local
的非本地对象基本上是static
所以这是有道理的。
现在, global
和thread
都没有常量初始化——所以两者都是零初始化,然后它们必须进行动态初始化。 到[basic.start.dynamic] !
如果变量是隐式或显式实例化的特化,则具有静态存储持续时间的非局部变量的动态初始化是无序的,如果变量是不是隐式或显式实例化的特化的内联变量,则是部分有序的,否则是有序的.
我们的变量都不是特化的,也都不是内联的。 所以两者都是有序的。
声明 D 在声明 E 之前是出现顺序的,如果
- D 出现在与 E 相同的翻译单元中,或
- 包含 E 的翻译单元对包含 D 的翻译单元有接口依赖,
在任何一种情况下,都在 E 之前。
我们的声明彼此之间不是按外观顺序排列的。
具有静态存储持续时间的非局部变量 V 和 W 的动态初始化顺序如下:
好的,子项目 1:
如果 V 和 W 有有序初始化并且 V 的定义在 W 的定义之前是外观有序的,或者如果 V 有部分有序初始化,W 没有无序初始化,并且对于 W 的每个定义 E 都存在一个定义 D的 V 使得 D 在 E 之前出现顺序,
不适用。 这是一个复杂的条件,但它并不适用。
否则,如果程序在初始化 V 或 W 之前启动了主线程以外的线程,则未指定 V 和 W 的初始化发生在哪个线程中; 如果它们发生在同一线程中,则初始化是无序的。
不,没有线程。
否则,V 和 W 的初始化顺序不确定。
我们走了。 global
和thread
是不确定的。
另请注意:
具有静态存储持续时间的非局部内联变量的动态初始化是在 main 的第一条语句之前排序还是延迟,由实现定义。
和:
具有线程存储期的非局部非内联变量的动态初始化是在线程的初始函数的第一条语句之前排序还是延迟,由实现定义。
没有保证,也没有任何形式的保证——至少目前是这样。
想象以下情况,您有另一个不相关的static
全局变量Z
在初始化期间使用您的thread_local
变量,或者甚至创建另一个线程并使用它 - 全部在其初始化期间。
现在碰巧这个static
全局变量Z
在你的静态全局变量global
之前被初始化。 这意味着必须在静态全局变量之前初始化thread_local
变量。
注意:目前,无法保证静态全局变量的初始化顺序 - C++ 的一个已知问题。 因此,如果您在另一个全局变量中使用一个全局变量,它可能会或可能不会导致错误 - 从技术上讲是 UB。 不要认为它以任何方式影响thread_local
变量,因为它们的初始化机制往往非常不同。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.