繁体   English   中英

主线程中的块范围静态与命名空间范围thread_local的初始化和销毁​​顺序

[英]Order of initialization and destruction of block-scope static vs. namespace-scope thread_local in main thread

我正在尝试理解在主线程的上下文中具有静态存储持续时间和线程局部存储持续时间的命名空间范围和块范围对象的初始化和销毁​​的排序规则。 考虑这两个类:

struct Foo {
    Foo() { std::cout << "Foo\n"; }
    ~Foo() { std::cout << "~Foo\n"; }
    static Foo &instance();
};
struct Bar {
    Bar() { std::cout << "Bar\n"; }
    ~Bar() { std::cout << "~Bar\n"; }
    static Bar &instance();
};

除了静态instance成员函数的实现之外,它们是相同的:

thread_local Foo t_foo;
Foo &Foo::instance() { return t_foo; }

Bar &Bar::instance() { static Bar s_bar; return s_bar; }

Bar是Meyers singleton,一个具有静态存储持续时间的块范围对象。

Foo的实例是具有线程局部存储持续时间的命名空间范围对象。

现在main功能:

int main() {
    Bar::instance();
    Foo::instance();
}

这是GCC 8.1.0和Clang 5.0.0的输出:

Bar
Foo
~Foo
~Bar

试一试: https//coliru.stacked-crooked.com/a/f83a9ec588aed921

我原本以为Foo会先构建,因为它在命名空间范围内。 我想允许实现推迟初始化直到对象的第一次使用。 我不知道它可以推迟到块范围静态初始化之后,但我可以忍受。

现在我颠倒main函数调用的顺序:

int main() {
    Foo::instance();
    Bar::instance();
}

这是输出:

Foo
Bar
~Foo
~Bar

现在我已经将第一次使用Foo实例的odr用于第一次调用Bar::instance ,初始化顺序正如我预期的那样。

但我认为对象应该按照初始化的相反顺序销毁,这似乎并没有发生。 我错过了什么?

关于静态和线程本地存储持续时间的对象的初始化和销毁​​,cppreference和标准说“程序启动时”,“线程启动时”,“程序结束时”和“当程序结束时”线程结束“,但这些概念在主线程的上下文中如何相互关联? 或者更确切地说,第一个线程和最后一个线程?

在我的“真实”问题中,记录器使用Foo (线程本地),并且Bar的基类的析构函数使用记录器,因此它是静态销毁订单惨败。 产生了其他线程,但是Bar (Meyers singleton)在主线程中被构造和销毁。 如果我能理解排序规则,那么我可以尝试解决“真实”问题,而不必仅仅随意尝试。

该标准保证Foo (线程本地存储)的销毁在Bar (静态存储)之前:

[basic.start.term] / 2

具有该线程内的线程存储持续时间的所有初始化对象的析构函数的完成强烈地发生在具有静态存储持续时间的任何对象的析构函数的启动之前。

但是,无法保证施工顺序。 该标准仅表示本地线程应在其第一次使用之前构建:

[basic.stc.thread] / 2

具有线程存储持续时间的变量应在其第一次使用之前初始化

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM