C++ 当 main() 退出时,分离的线程会发生什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19744250/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What happens to a detached thread when main() exits?
提问by Marc Mutz - mmutz
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.
假设我开始 astd::thread
然后detach()
它,所以线程继续执行,即使std::thread
曾经代表它的那个超出了范围。
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.
进一步假设程序没有可靠的加入分离线程1 的协议,因此分离线程在main()
退出时仍然运行。
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.
我在标准中找不到任何内容(更准确地说,在 N3797 C++14 草案中),它描述了应该发生的情况,1.10 和 30.3 都没有包含相关的措辞。
1Another, 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.
1另一个可能等效的问题是:“是否可以再次加入分离的线程”,因为无论您发明要加入的协议是什么,信号部分都必须在线程仍在运行时完成,并且操作系统调度程序可能决定在执行信令后立即让线程休眠一个小时,而接收端无法可靠地检测到线程实际上已完成。
If running out of main()
with detached threads running is undefined behaviour, then anyuse of std::thread::detach()
is undefined behaviour unless the main thread never exits2.
如果运行的main()
分离线程用完是未定义的行为,那么任何使用std::thread::detach()
都是未定义的行为,除非主线程永远不会退出2。
Thus, running out of main()
with detached threads running must have definedeffects. The question is: where(in the C++ standard, not POSIX, not OS docs, ...) are those effects defined.
因此,运行的main()
分离线程用完必须具有定义的效果。问题是:其中(在C ++标准,不POSIX,不OS文档,......)都处于所定义的那些的效果。
2A detached thread cannot be joined (in the sense of std::thread::join()
). You canwait 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 afterthe 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?
2分离的线程不能连接(在 的意义上std::thread::join()
)。您可以等待分离线程的结果(例如,通过未来 from std::packaged_task
,或通过计数信号量或标志和条件变量),但这并不能保证线程已完成执行。事实上,除非你把信令部分进入线程的第一个自动对象的析构函数,也将在一般情况下,是代码(析构函数),其运行后的信号代码。如果操作系统调度主线程消耗结果并在分离的线程完成运行所述析构函数之前退出,那么 ^Wi 将发生什么?
采纳答案by Marc Mutz - mmutz
The answer to the original question "what happens to a detached thread when main()
exits" is:
原始问题“main()
退出时分离的线程会发生什么”的答案是:
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.
它继续运行(因为标准没有说它已停止),并且这是明确定义的,只要它既不涉及其他线程的 (automatic|thread_local) 变量也不涉及静态对象。
This appears to be allowed to allow thread managers as static objects (note in [basic.start.term]/4says as much, thanks to @dyp for the pointer).
这似乎允许线程管理器作为静态对象([basic.start.term]/4 中的注释说明了这一点,感谢@dyp 提供了指针)。
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—excludescondition_variable
(it's implementation-defined whether that is save to use in a signal handler, because it's not part of <atomic>
).
当静态对象的销毁完成时会出现问题,因为然后执行进入一个只能执行信号处理程序中允许的代码的状态([basic.start.term]/1, 1st sentence)。在 C++ 标准库中,只有<atomic>
库([support.runtime]/9, 2nd sentence)。特别是,一般来说,排除condition_variable
(它是实现定义的是否保存以在信号处理程序中使用,因为它不是 的一部分<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()
, ...).
是的,使用*_at_thread_exit
函数系列 ( 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-beforethe 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.
如问题的脚注[2]所指出的,信令条件变量或信号量或原子计数器不足以加入分离的线程(在确保其执行结束读出已-之前发生的接收由等待线程发出信号),因为一般来说,在例如notify_all()
条件变量的 a之后将执行更多代码,特别是自动和线程局部对象的析构函数。
Running the signalling as the last thing the thread does (afterdestructors of automatic and thread-local objects has-happened) is what the _at_thread_exit
family of functions was designed for.
作为线程做的最后一件事(在自动和线程本地对象的析构函数发生之后)运行信号是_at_thread_exit
函数系列的设计目的。
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 ormake the detached thread execute onlycode that would be safe for a signal handler, too.
因此,为了在没有高于标准要求的任何实现保证的情况下避免未定义的行为,您需要(手动)将分离的线程与_at_thread_exit
执行信号的函数连接起来,或者使分离的线程仅执行对以下情况安全的代码也是信号处理程序。
回答by Sam
Detaching Threads
分离线程
According to std::thread::detach
:
Separates the thread of execution from the thread object, allowing execution to continue independently. Any allocated resources will be freed once the thread exits.
将执行线程与线程对象分开,允许独立继续执行。一旦线程退出,任何分配的资源都将被释放。
From pthread_detach
:
The pthread_detach() function shall indicate to the implementation that storage for the thread can be reclaimed when that thread terminates. If thread has not terminated, pthread_detach() shall not cause it to terminate. The effect of multiple pthread_detach() calls on the same target thread is unspecified.
pthread_detach() 函数应向实现指示该线程终止时可以回收该线程的存储。如果线程尚未终止,则 pthread_detach() 不应使其终止。未指定对同一目标线程进行多次 pthread_detach() 调用的效果。
Detaching threads is mainly for saving resources, in case the application does not need to wait for a thread to finish (e.g. daemons, which must run until process termination):
分离线程主要是为了节省资源,以防应用程序不需要等待线程完成(例如守护进程,它必须运行到进程终止):
- To free the application side handle: One can let a
std::thread
object go out of scope without joining, what normally leads to a call tostd::terminate()
on destruction. - To allow the OS to cleanup the thread specific resources (TCB) automatically as soon as the thread exits, because we explicitly specified, that we aren't interested in joining the thread later on, thus, one cannot join an already detached thread.
- 释放应用程序端句柄:可以
std::thread
在不加入的情况下让对象超出范围,这通常会导致调用std::terminate()
on 销毁。 - 允许操作系统在线程退出时自动清理线程特定资源 ( TCB),因为我们明确指定,我们对稍后加入线程不感兴趣,因此,不能加入已经分离的线程。
Killing Threads
杀死线程
The behavior on process termination is the same as the one for the main thread, which could at least catch some signals. Whether or not other threads can handle signals is not that important, as one could join or terminate other threads within the main thread's signal handler invocation. (Related question)
进程终止的行为与主线程的行为相同,至少可以捕获一些信号。其他线程是否可以处理信号并不那么重要,因为可以在主线程的信号处理程序调用中加入或终止其他线程。(相关问题)
As already stated, any thread, whether detached or not, will die with its process on most OSes. The process itself can be terminated by raising a signal, by calling exit()
or by returning from the main function. However, C++11 cannot and does not try to define the exact behaviour of the underlying OS, whereas the developers of a Java VM can surely abstract such differences to some extent. AFAIK, exotic process and threading models are usually found on ancient platforms (to which C++11 probably won't be ported) and various embedded systems, which could have a special and/or limited language library implementation and also limited language support.
如前所述,任何线程,无论是否分离,在大多数操作系统上都会随着其进程而死亡。进程本身可以通过发出信号、调用exit()
或从主函数返回来终止。然而,C++11 不能也不会尝试定义底层操作系统的确切行为,而 Java VM 的开发人员肯定可以在某种程度上抽象出这些差异。AFAIK,奇异的进程和线程模型通常存在于古老的平台(C++11 可能不会移植到该平台)和各种嵌入式系统上,这些系统可能具有特殊和/或有限的语言库实现以及有限的语言支持。
Thread Support
线程支持
If threads aren't supported std::thread::get_id()
should return an invalid id (default constructed std::thread::id
) as there's a plain process, which does not need a thread object to run and the constructor of a std::thread
should throw a std::system_error
. This is how I understand C++11 in conjunction with today's OSes. If there's an OS with threading support, which doesn't spawn a main thread in its processes, let me know.
如果不支持线程std::thread::get_id()
应该返回一个无效的 id(默认构造std::thread::id
),因为有一个普通进程,它不需要线程对象来运行并且 a 的构造函数std::thread
应该抛出 a std::system_error
。这就是我对 C++11 和当今操作系统的理解。如果有一个支持线程的操作系统,它不会在其进程中生成主线程,请告诉我。
Controlling Threads
控制线程
If one needs to keep control over a thread for proper shutdown, one can do that by using sync primitives and/or some sort of flags. However, In this case, setting a shutdown flag followed by a join is the way I prefer, since there's no point in increasing complexity by detaching threads, as the resources would be freed at the same time anyway, where the few bytes of the std::thread
object vs. higher complexity and possibly more sync primitives should be acceptable.
如果需要保持对线程的控制以正确关闭,则可以通过使用同步原语和/或某种标志来做到这一点。但是,在这种情况下,设置关闭标志后跟连接是我更喜欢的方式,因为通过分离线程来增加复杂性是没有意义的,因为无论如何都会同时释放资源,其中std::thread
对象的几个字节与更高的复杂性和可能更多的同步原语相比应该是可以接受的。
回答by kgvinod
Consider the following code:
考虑以下代码:
#include <iostream>
#include <string>
#include <thread>
#include <chrono>
void thread_fn() {
std::this_thread::sleep_for (std::chrono::seconds(1));
std::cout << "Inside thread function\n";
}
int main()
{
std::thread t1(thread_fn);
t1.detach();
return 0;
}
Running it on a Linux system, the message from the thread_fn is never printed. The OS indeed cleans up thread_fn()
as soon as main()
exits. Replacing t1.detach()
with t1.join()
always prints the message as expected.
在 Linux 系统上运行它,永远不会打印来自 thread_fn 的消息。操作系统确实会thread_fn()
在main()
退出后立即清理。替换t1.detach()
为t1.join()
始终按预期打印消息。
回答by Caesar
The fate of the thread after the program exits is undefined behavior. But a modern operating system will clean up all threads created by the process on closing it.
程序退出后线程的命运是未定义的行为。但是现代操作系统会在关闭进程时清除进程创建的所有线程。
When detaching an std::thread
, these three conditions will continue to hold:
分离时std::thread
,这三个条件将继续保持:
*this
no longer owns any threadjoinable()
will always equal tofalse
get_id()
will equalstd::thread::id()
*this
不再拥有任何线程joinable()
将永远等于false
get_id()
将等于std::thread::id()
回答by funk
When the main thread (that is, the thread that runs the main() function) terminates, then the process terminates and all other threads stop.
当主线程(即运行 main() 函数的线程)终止时,进程终止,所有其他线程停止。
Reference: https://stackoverflow.com/a/4667273/2194843
回答by yshi
To allow other threads to continue execution, the main thread should terminate by calling pthread_exit() rather than exit(3). It's fine to use pthread_exit in main. When pthread_exit is used, the main thread will stop executing and will remain in zombie(defunct) status until all other threads exit. If you are using pthread_exit in main thread, cannot get return status of other threads and cannot do clean-up for other threads (could be done using pthread_join(3)). Also, it's better to detach threads(pthread_detach(3)) so that thread resources are automatically released on thread termination. The shared resources will not be released until all threads exit.
为了允许其他线程继续执行,主线程应该通过调用 pthread_exit() 而不是 exit(3) 来终止。在 main 中使用 pthread_exit 没问题。当使用 pthread_exit 时,主线程将停止执行并保持僵尸(defunct)状态,直到所有其他线程退出。如果在主线程中使用 pthread_exit,则无法获取其他线程的返回状态,也无法对其他线程进行清理(可以使用 pthread_join(3) 完成)。此外,最好分离线程(pthread_detach(3)),以便在线程终止时自动释放线程资源。直到所有线程退出,共享资源才会被释放。