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.
假设我正在启动一个std::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.
进一步假设程序没有连接分离的thread1的可靠协议,因此当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都没有包含相关的措辞。
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.
1另一个,可能是等价的,问题是:“一个独立的线程能再次加入”,因为无论你发明加入协议,信号部分必须完成线程仍在运行的时候,和操作系统调度器可能决定把线程后睡了一个小时没有为接收端信号进行可靠地检测到线程实际上完成了。
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.
如果在运行分离线程的情况下耗尽main()是未定义的行为,那么使用std:::thread: detach()是未定义的行为,除非主线程从不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.
因此,使用独立的线程运行的main()必须具有定义的效果。问题是:(在c++标准中,不是POSIX,不是OS docs,…)这些效果是在哪里定义的。
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?
不能连接分离的线程(从std:::thread: join()的意义来看)。您可以等待来自分离线程的结果(例如通过来自std::packaged_task的未来,或者通过计数信号量、标志和条件变量),但这并不保证线程已经完成执行。实际上,除非将信号部分放入线程的第一个自动对象的析构函数中,否则,通常会有代码(析构函数)在信号代码后运行。如果操作系统计划消费主线程分离线程结束之前的结果和退出运行析构函数表示什么^威斯康星州定义呢?
5 个解决方案
#1
34
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.
它将继续运行(因为标准并没有说它已经停止),而且这是定义良好的,只要它不触及其他线程或静态对象的(自动|thread_local)变量。
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).
这似乎允许线程管理器作为静态对象(请注意[basic.start])。/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—excludes condition_variable
(it's implementation-defined whether that is save to use in a signal handler, because it's not part of <atomic>
).
当静态对象的销毁完成时,问题就出现了,因为执行进入了只有信号处理程序中允许的代码可以执行的机制([basic.start])。项]/ 1,1句)。在c++标准库中,这只是
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-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.
脚注[2]中提到的问题,信号一个条件变量或信号灯或原子计数器是不够加入分离线程(在确保其执行结束的感觉以前发生过接收的信号通过一个等待线程),因为一般来说,将会有更多的代码后执行如notify_all()的一个条件变量,特别是自动和线程本地对象的析构函数。
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.
作为线程做的最后一件事(在自动和线程本地对象的析构函数发生之后)运行信号是_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 or make the detached thread execute only code that would be safe for a signal handler, too.
所以,为了避免未定义的行为没有任何实现担保以上标准要求什么,你需要与_at_thread_exit(手动)加入一个独立的线程函数只做信号或使分离线程执行的代码是安全的一个信号处理程序,。
#2
34
Detaching Threads
According to std::thread::detach
:
根据std::线程::分离:
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
:
从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. - 要释放应用程序端句柄:可以让std::thread对象在不连接的情况下超出范围,通常会导致在销毁时调用std::terminate()。
- 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.
- 为了让操作系统在线程退出时自动清除线程特定的资源(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虚拟机的开发人员在一定程度上可以抽象出这些差异。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),因为有一个普通进程,它不需要运行线程对象,std:::thread的构造函数应该抛出std::system_error。这就是我如何理解c++ 11与今天的OSes结合在一起的原因。如果有一个支持线程的操作系统,它的进程中没有产生主线程,请让我知道。
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::线程对象与更高的复杂性和可能应该接受更多的同步原语。
#3
15
The fate of the thread after the program exits is undefined behaviour. 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::线程时,这三个条件将继续保持。
-
*this
no longer owns any thread - 这个不再拥有任何线程
- joinable() will always equal to false
- joinable()始终等于false
- get_id() will equal std::thread::id()
- get_id()等于std::线程::id()
#4
15
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的消息不会被打印出来。只要main()退出,OS就会清理thread_fn()。用t1.join()替换t1.detach()始终按预期打印消息。
#5
5
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://*.com/a/4667273/2194843
参考:https://*.com/a/4667273/2194843
#1
34
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.
它将继续运行(因为标准并没有说它已经停止),而且这是定义良好的,只要它不触及其他线程或静态对象的(自动|thread_local)变量。
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).
这似乎允许线程管理器作为静态对象(请注意[basic.start])。/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—excludes condition_variable
(it's implementation-defined whether that is save to use in a signal handler, because it's not part of <atomic>
).
当静态对象的销毁完成时,问题就出现了,因为执行进入了只有信号处理程序中允许的代码可以执行的机制([basic.start])。项]/ 1,1句)。在c++标准库中,这只是
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-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.
脚注[2]中提到的问题,信号一个条件变量或信号灯或原子计数器是不够加入分离线程(在确保其执行结束的感觉以前发生过接收的信号通过一个等待线程),因为一般来说,将会有更多的代码后执行如notify_all()的一个条件变量,特别是自动和线程本地对象的析构函数。
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.
作为线程做的最后一件事(在自动和线程本地对象的析构函数发生之后)运行信号是_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 or make the detached thread execute only code that would be safe for a signal handler, too.
所以,为了避免未定义的行为没有任何实现担保以上标准要求什么,你需要与_at_thread_exit(手动)加入一个独立的线程函数只做信号或使分离线程执行的代码是安全的一个信号处理程序,。
#2
34
Detaching Threads
According to std::thread::detach
:
根据std::线程::分离:
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
:
从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. - 要释放应用程序端句柄:可以让std::thread对象在不连接的情况下超出范围,通常会导致在销毁时调用std::terminate()。
- 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.
- 为了让操作系统在线程退出时自动清除线程特定的资源(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虚拟机的开发人员在一定程度上可以抽象出这些差异。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),因为有一个普通进程,它不需要运行线程对象,std:::thread的构造函数应该抛出std::system_error。这就是我如何理解c++ 11与今天的OSes结合在一起的原因。如果有一个支持线程的操作系统,它的进程中没有产生主线程,请让我知道。
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::线程对象与更高的复杂性和可能应该接受更多的同步原语。
#3
15
The fate of the thread after the program exits is undefined behaviour. 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::线程时,这三个条件将继续保持。
-
*this
no longer owns any thread - 这个不再拥有任何线程
- joinable() will always equal to false
- joinable()始终等于false
- get_id() will equal std::thread::id()
- get_id()等于std::线程::id()
#4
15
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的消息不会被打印出来。只要main()退出,OS就会清理thread_fn()。用t1.join()替换t1.detach()始终按预期打印消息。
#5
5
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://*.com/a/4667273/2194843
参考:https://*.com/a/4667273/2194843