当main()退出时,脱离的线程会发生什么? [英] What happens to a detached thread when main() exits?

查看:441
本文介绍了当main()退出时,脱离的线程会发生什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我开始一个 std :: thread ,然后 detach()即使已经代表它的 std :: thread ,也会执行。



程序没有可靠的协议加入分离的线程 1 ,因此当 main()退出时,分离的线程仍然运行。

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



1 另一个可能是等价的问题是:一个分离的线程可以再次连接,因为无论你发明什么协议加入,部分将必须在线程仍在运行时完成,并且OS调度器可以决定在刚好在执行信令之后使线程休眠一小时,而不能使接收端可靠地检测到线程实际完成。 / p>

如果运行 main()时运行的分离线程是未定义的行为,则任何使用 std :: thread :: detach()是未定义的行为,除非主线程不会退出 2



因此,运行带有分离线程的 main()必须具有定义的效果。问题是:其中(在 C ++标准,而不是POSIX,而不是操作系统文档...)是定义的效果。



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

解决方案

原始问题当 main()退出时发生什么情况的答案是:



它继续运行(因为标准没有说它被停止),并且定义良好,只要它既不触及其他线程的变量(自动| thread_local)也不触及静态对象。



这似乎允许线程管理器作为静态对象(注意在 [basic.start.term] / 4 中说的话,感谢@dyp for



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



除非你在这一点上解开了堆栈,很难看到如何避免未定义的行为。



第二个问题可以分离线程再次加入的答案是:



是的,使用 * _ at_thread_exit 系列函数( notify_all_at_thread_exit() std :: promise :: set_value_at_thread_exit(),...)。



如问题的脚注[2]变量或信号量或原子计数器不足以加入分离的线程(在确保其执行的结束在由等待线程接收所述信令之前) ,因为,一般来说,会有更多的代码执行后a notify_all()条件变量,特别是自动和线程本地对象的析构函数。



作为线程所做的最后一件事(之后自动和线程局部对象的发现的信号)是什么 _at_thread_exit 函数家族是为。



设计的。因此,为了避免未定义的行为在没有任何实现保证以上的标准要求, (手动)使用 _at_thread_exit 函数执行信号使分离的线程仅执行 代码对于信号处理程序也是安全的。


Assume I'm starting a std::thread and then detach() it, so the thread continues executing even though the std::thread that once represented it, goes out of scope.

Assume further that the program does not have a reliable protocol for joining the detached thread1, so the detached thread still runs when main() exits.

I cannot find anything in the standard (more precisely, in the N3797 C++14 draft), which describes what should happen, neither 1.10 nor 30.3 contain pertinent wording.

1 Another, probably equivalent, question is: "can a detached thread ever be joined again", because whatever protocol you're inventing to join, the signalling part would have to be done while the thread was still running, and the OS scheduler might decide to put the thread to sleep for an hour just after signalling was performed with no way for the receiving end to reliably detect that the thread actually finished.

If running out of main() with detached threads running is undefined behaviour, then any use of std::thread::detach() is undefined behaviour unless the main thread never exits2.

Thus, running out of main() with detached threads running must have defined effects. The question is: where (in the C++ standard, not POSIX, not OS docs, ...) are those effects defined.

2 A detached thread cannot be joined (in the sense of std::thread::join()). You can wait for results from detached threads (e.g. via a future from std::packaged_task, or by a counting semaphore or a flag and a condition variable), but that doesn't guarantee that the thread has finished executing. Indeed, unless you put the signalling part into the destructor of the first automatic object of the thread, there will, in general, be code (destructors) that run after the signalling code. If the OS schedules the main thread to consume the result and exit before the detached thread finishes running said destructors, what will^Wis defined to happen?

解决方案

The answer to the original question "what happens to a detached thread when main() exits" is:

It continues running (because the standard doesn't say it is stopped), and that's well-defined, as long as it touches neither (automatic|thread_local) variables of other threads nor static objects.

This appears to be allowed to allow thread managers as static objects (note in [basic.start.term]/4 says as much, thanks to @dyp for the pointer).

Problems arise when the destruction of static objects has finished, because then execution enters a regime where only code allowed in signal handlers may execute ([basic.start.term]/1, 1st sentence). Of the C++ standard library, that is only the <atomic> library ([support.runtime]/9, 2nd sentence). In particular, that—in general—excludes condition_variable (it's implementation-defined whether that is save to use in a signal handler, because it's not part of <atomic>).

Unless you've unwound your stack at this point, it's hard to see how to avoid undefined behaviour.

The answer to the second question "can detached threads ever be joined again" is:

Yes, with the *_at_thread_exit family of functions (notify_all_at_thread_exit(), std::promise::set_value_at_thread_exit(), ...).

As noted in footnote [2] of the question, signalling a condition variable or a semaphore or an atomic counter is not sufficient to join a detached thread (in the sense of ensuring that the end of its execution has-happened-before the receiving of said signalling by a waiting thread), because, in general, there will be more code executed after e.g. a notify_all() of a condition variable, in particular the destructors of automatic and thread-local objects.

Running the signalling as the last thing the thread does (after destructors of automatic and thread-local objects has-happened) is what the _at_thread_exit family of functions was designed for.

So, in order to avoid undefined behaviour in the absence of any implementation guarantees above what the standard requires, you need to (manually) join a detached thread with an _at_thread_exit function doing the signalling or make the detached thread execute only code that would be safe for a signal handler, too.

这篇关于当main()退出时,脱离的线程会发生什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆