小编典典

当 main() 退出时,分离的线程会发生什么?

all

假设我开始 astd::thread然后detach()它,所以线程继续执行,即使std::thread曾经代表它,超出范围。

进一步假设程序没有可靠的协议来加入分离线程1main() ,因此分离线程在退出时仍然运行。

我在标准中找不到任何东西(更准确地说,在 N3797 C++14 草案中),它描述了应该发生的事情,1.10 和 30.3 都没有包含相关的措辞。

1另一个可能等效的问题是:“可以再次加入分离的线程吗”,因为无论您发明什么协议来加入,信号部分都必须在线程仍在运行时完成,并且操作系统调度程序可能决定在执行信号后立即让线程休眠一个小时,而接收端无法可靠地检测到线程实际上已完成。

如果在运行main()分离线程时用完是未定义的行为,那么 任何 使用
ofstd::thread::detach()都是未定义的行为,除非主线程永远不会退出2。

因此,运行main()分离线程的用完必须具有 定义 的效果。问题是: 在哪里 (在 C++ 标准 中,不是 POSIX,不是 OS
文档,......)是那些定义的效果。

2无法连接分离的线程(在 的意义上std::thread::join())。您 可以 等待来自分离线程的结果(例如,通过来自
的未来std::packaged_task,或通过计数信号量或标志和条件变量),但这并不能保证 线程已经完成执行
。实际上,除非您将信号部分放入线程的第一个自动对象的析构函数中,否则通常 会有 代码(析构函数)在信号代码 之后运行。
如果操作系统安排主线程在分离线程完成运行所述析构函数之前使用结果并退出,那么^Wi 定义会发生什么?


阅读 81

收藏
2022-08-15

共1个答案

小编典典

main()原始问题“退出时分离的线程会发生什么”的答案是:

它继续运行(因为标准没有说它停止),这是定义明确的,只要它既不触及其他线程的 (automatic|thread_local) 变量也不触及静态对象。

这似乎被允许允许线程管理器作为静态对象(在 [basic.start.term]/4 中的注释说了很多,感谢@dyp 的指针)。

当静态对象的销毁完成时会出现问题,因为执行会进入一个只有信号处理程序中允许的代码才能执行的状态( [basic.start.term]/1,第 1 句
)。在 C++ 标准库中,这只是<atomic>库( [support.runtime]/9,第 2 句 )。特别是,“一般” 不包括
condition_variable(它是实现定义的是否保存以在信号处理程序中使用,因为它不是 的一部分<atomic>)。

除非您此时已展开堆栈,否则很难看出如何避免未定义的行为。

第二个问题“分离的线程是否可以再次连接”的答案是:

是的,使用*_at_thread_exit函数族 ( notify_all_at_thread_exit(),
std::promise::set_value_at_thread_exit(), …)。

如问题的脚注 [2] 中所述,发出条件变量或信号量或原子计数器的信号不足以加入分离的线程(在确保其执行结束的意义上,
接收到所述由等待线程发出的信号),因为一般情况下,在例如notify_all()条件变量的 a
之后会执行更多的代码,特别是自动和线程局部对象的析构函数。

运行信号作为线程做的最后一件事( 自动和线程本地对象的析构函数 发生 之后)是_at_thread_exit函数系列的设计目的。

因此,为了避免在没有超出标准要求的任何实现保证的情况下出现未定义的行为,您需要(手动)将分离的线程与_at_thread_exit执行信号的函数连接起来,
或者 使分离的线程 执行对安全的代码也是一个信号处理程序。

2022-08-15