我有以下情况:在标题“test.hpp”中,我定义了:class ObjectA { public: ObjectA(); ~ObjectA(); static ObjectA & get_A();};class ObjectB { public: ~ObjectB(); static ObjectB & get_B(); void do_cleanup();};在单独的编译单元中,我实现了 ObjectB:#include "test.hpp"#include <iostream>ObjectB::~ObjectB() { std::cout<<"ObjectB dtor"<<std::endl;}ObjectB & ObjectB::get_B() { thread_local ObjectB b_instance; return b_instance;}void ObjectB::do_cleanup() { std::cout<<"Clearing up B garbage..."<<std::endl;}对象A:#include "test.hpp"#include <iostream>ObjectA::ObjectA() { ObjectB::get_B(); <--dummy call to initialize thread_local ObjectB;}ObjectA::~ObjectA() { std::cout<<"ObjectA dtor"<<std::endl; ObjectB::get_B().do_cleanup(); // <-- is this undefined behaviour??}ObjectA & ObjectA::get_A() { thread_local ObjectA a_instance; return a_instance;}最后是一个测试 main():#include <thread>#include "test.hpp"int main() { std::thread check([](){ ObjectA::get_A(); //<--dummy call just to initialize thread_local object. }); check.join(); return 0;}上面的程序是否表现良好或正在访问 objectB,它具有来自 ObjectA 析构函数的 thread_local 存储,该析构函数也具有 thread_local 存储未定义的行为?如果是这样,它为什么会损坏,我该如何修复? most related question I found[编辑,@Sonts 回答]在实际用例中,A 类是模板,一个相当复杂的类,而 B 类只是很大。 A 对象使用 shared_ptr 保存对 B 的引用,并且根据需要访问 B 的 thread_locals。 (A 在主线程中构造并传递给工作线程) ObjectB::get_B() 因此在 ObjectA::get_A() 被调用之前可能不会被工作线程调用。 最佳答案 规范说了几件关于生命周期的事情: Storage class specifiers线程存储持续时间。对象的存储在线程开始时分配,在线程结束时释放。每个线程都有自己的对象实例。 Termination如果构造函数的 完成 或具有线程存储期的对象的动态初始化顺序在另一个之前,则第二个析构函数的完成顺序在第一个析构函数的启动之前。现在回到你的代码。你构造A,在你构造B的构造函数中。因此,B构造函数的完成发生在A构造函数的完成之前。如上所述,当线程即将退出时,它会先销毁A,然后是B。根据规范的字母,您的代码是可以的。实际上,我不确定 C++ 编译器是否将规范实现到这种详细程度。如果我正在编写该代码,我不会以这种方式使用 thread_local 对象。相反,我会将 B 放在 A 的非静态字段中。 它比依赖语言标准的这种细微差别更简单,IMO 更可靠。关于C++11 thread_local 析构函数行为,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51954025/
10-11 16:54