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
所以这是有道理的。
现在,global
和 thread
都没有常量初始化 - 因此两者都是零初始化,然后必须进行动态初始化。到 [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.
好了。 global
和 thread
的顺序不确定。
另请注意:
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.
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
所以这是有道理的。
现在,global
和 thread
都没有常量初始化 - 因此两者都是零初始化,然后必须进行动态初始化。到 [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.
好了。 global
和 thread
的顺序不确定。
另请注意:
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.