thread_local 与全局变量的初始化顺序

initialization order of thread_local vs. global variables

C.h:

#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] Static initialization

Variables with static storage duration are initialized as a consequence of program initiation. Variables with thread storage duration are initialized as a consequence of thread execution.

按理说 "program initiation" 发生在 "thread execution" 之前,但由于这两个表达式仅出现在标准中的那个地方,我正在寻求实际语言律师的建议。

没有任何保证,也不可能有任何形式的保证——至少目前是这样。

想象下面的情况,你有另一个不相关的 static 全局变量 Z 在初始化期间使用你的 thread_local 变量,或者说,甚至创建另一个线程并使用它 - 所有在它的初始化。

现在恰好这个 static 全局变量 Z 在您的静态全局变量 global 之前被初始化。这意味着必须在静态全局变量之前初始化 thread_local 变量。

注意:目前无法保证静态全局变量的初始化顺序 - 这是 C++ 的一个已知问题。因此,如果您在另一个全局变量中使用一个全局变量,它可能会或可能不会导致错误——从技术上讲是一个 UB。不要认为它会以任何方式影响 thread_local 变量,因为它们的初始化机制往往非常不同。

我将使用 C++20 工作草案,因为其中的措辞更清晰一些,尽管 none 实际规则已经更改。

首先,就非本地而言,thread_local 的行为基本上类似于 static[basic.stc.thread]/2:

[ Note: A variable with thread storage duration is initialized as specified in [basic.start.static], [basic.start.dynamic], and [stmt.dcl] and, if constructed, is destroyed on thread exit ([basic.start.term]). — end note ]

是的,这是一张便条。但是声明 thread_local 的非局部对象基本上是 static 所以这是有道理的。

现在,globalthread 都没有常量初始化 - 因此两者都是零初始化,然后必须进行动态初始化。到 [basic.start.dynamic]!

Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an implicitly or explicitly instantiated specialization, is partially-ordered if the variable is an inline variable that is not an implicitly or explicitly instantiated specialization, and otherwise is ordered.

我们的变量都不是特化的,它们都不是内联的。所以两者都是 有序.

A declaration D is appearance-ordered before a declaration E if

  • D appears in the same translation unit as E, or
  • the translation unit containing E has an interface dependency on the translation unit containing D,

in either case prior to E.

我们的声明彼此之间没有外观顺序。

Dynamic initialization of non-local variables V and W with static storage duration are ordered as follows:

好的,子项目符号 1:

If V and W have ordered initialization and the definition of V is appearance-ordered before the definition of W, or if V has partially-ordered initialization, W does not have unordered initialization, and for every definition E of W there exists a definition D of V such that D is appearance-ordered before E,

不适用。这是一个复杂的条件,但不适用。

Otherwise, if the program starts a thread other than the main thread before either V or W is initialized, it is unspecified in which threads the initializations of V and W occur; the initializations are unsequenced if they occur in the same thread.

不,没有线程。

Otherwise, the initializations of V and W are indeterminately sequenced.

好了。 globalthread 的顺序不确定。


另请注意:

It is implementation-defined whether the dynamic initialization of a non-local inline variable with static storage duration is sequenced before the first statement of main or is deferred.

和:

It is implementation-defined whether the dynamic initialization of a non-local non-inline variable with thread storage duration is sequenced before the first statement of the initial function of a thread or is deferred.