静态初始化jthread调用std::stacktrace_entry::description时发生死锁
单例线程退出时死锁问题
以下C++代码在退出main()函数时会发生死锁:
#include <stacktrace> #include <iostream> #include <thread> #include <semaphore> #include <chrono> using namespace std::chrono_literals; struct Singleton { Singleton() { worker = std::jthread{ [this] { sema.acquire(); for (auto& e : trace) { std::this_thread::sleep_for(50ms); std::cout << e.description() << std::endl; } } }; } std::binary_semaphore sema{ 0 }; std::stacktrace trace; std::jthread worker; }; std::stacktrace g() { return std::stacktrace::current(); } std::stacktrace f() { return g(); } Singleton& get() { static Singleton sing; return sing; } int main(int argc, char** argv) { get().trace = f(); get().sema.release(); std::this_thread::sleep_for(350ms); return 0; }
具体表现为,调用description()时,CRT代码尝试获取临界区时发生死锁。
我推测description()会调用依赖CRT全局对象的代码,而这些对象由临界区管理。可能的原因有两个:
- 该全局对象在
std::jthread析构函数执行前,就随main退出被销毁了; - 退出
main时,主线程和工作线程同时进入了同一个临界区。
想请教这段代码中哪部分用法导致了未定义行为(UB)?
背景说明:
这是从一个大型代码库中提炼的最小复现案例,原代码库中用单例管理包含工作线程和无锁队列的日志通道对象。
编辑说明:
代码中的sleep_for仅用于演示,并非必需,也不是为了修复竞态条件。即使移除这些调用,死锁现象依然存在。
内容的提问来源于stack exchange,提问作者chili
相关产品推荐
相关产品推荐

