Mar*_*utz 142 c++ multithreading exit c++11 stdthread
假设我正在启动a std::thread
然后detach()
它,所以线程继续执行,即使std::thread
它曾经表示它,超出范围.
进一步假设该程序不具有用于接合分离的线程一个可靠的协议1,所以分离线程仍然运行时main()
退出.
我在标准中找不到任何东西(更准确地说,在N3797 C++ 14草案中),它描述了应该发生的事情,1.10和30.3都没有包含相关的措辞.
1另一个可能是等同的问题是:"可以再次连接一个分离的线程",因为你要发明加入的协议,信号部分必须在线程仍在运行时完成,并且OS调度程序可能决定在执行信令之后让线程休眠一小时,接收端无法可靠地检测到线程实际完成.
如果用完main()
了卸下运行的线程是不确定的行为,那么任何使用的std::thread::detach()
是除非主线程永远不会退出未定义行为2.
因此,运行main()
脱离线程的耗尽必须具有已定义的效果.问题是:其中(在C++标准,不POSIX,不OS文档,......)都处于所定义的那些的效果.
2分离的线程不能加入(在感std::thread::join()
).您可以等待分离线程的结果(例如,通过未来std::packaged_task
,或通过计数信号量或标志和条件变量),但这并不能保证线程已完成执行.事实上,除非你把信令部分进入线程的第一个自动对象的析构函数,也将在一般情况下,是运行的代码(析构函数)后的信号代码.如果操作系统安排主线程使用结果并在分离的线程完成运行所述析构函数之前退出,^ Wis定义会发生什么?
Mar*_*utz 44
原始问题" main()
退出时分离线程会发生什么"的答案是:
它继续运行(因为标准并没有说它已停止),并且这是定义良好的,只要它既不接触其他线程的(自动| thread_local)变量也不接触静态对象.
这似乎允许允许线程管理器作为静态对象(注意[basic.start.term]/4中的注释,因为指针的@dyp).
当静态对象的破坏完成时会出现问题,因为执行进入只有信号处理程序允许的代码可以执行的状态([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]所述,发信号通知条件变量或信号量或原子计数器不足以加入分离的线程(在确保其执行结束已发生的意义上 - 在接收之前)通过等待线程表示信号),因为通常在例如notify_all()
条件变量之后将执行更多代码,特别是自动和线程局部对象的析构函数.
运行信号作为线程的最后一件事(在自动和线程局部对象的析构函数发生之后)是_at_thread_exit
函数族的设计目的.
因此,为了避免在没有任何高于标准要求的实现保证的情况下未定义的行为,您需要(手动)使用_at_thread_exit
执行信令的函数连接分离的线程,或者使分离的线程仅执行对于安全的代码信号处理程序也是.
Sam*_*Sam 40
将执行线程与线程对象分开,允许执行独立继续.线程退出后,将释放任何已分配的资源.
pthread_detach()函数应向实现指示当该线程终止时可以回收线程的存储.如果线程没有终止,pthread_detach()不会导致它终止.未指定多个pthread_detach()调用对同一目标线程的影响.
分离线程主要用于节省资源,以防应用程序不需要等待线程完成(例如守护进程,必须运行直到进程终止):
std::thread
对象超出范围而不加入,通常会导致std::terminate()
对销毁的调用.进程终止时的行为与主线程的行为相同,这至少可以捕获一些信号.其他线程是否可以处理信号并不重要,因为可以在主线程的信号处理程序调用中加入或终止其他线程.(相关问题)
如前所述,任何线程,无论是否分离,都会在大多数操作系统上死掉.可以通过调用信号,通过调用exit()
或从主函数返回来终止进程本身.但是,C++ 11不能也不会尝试定义底层操作系统的确切行为,而Java VM的开发人员肯定可以在一定程度上抽象出这种差异.AFAIK,异国情调的过程和线程模型通常可以在古代平台上找到(C++ 11可能不会被移植到其上)和各种嵌入式系统,它们可能具有特殊的和/或有限的语言库实现以及有限的语言支持.
如果不支持线程,则std::thread::get_id()
应该返回一个无效的id(默认构造std::thread::id
),因为有一个普通的进程,它不需要运行一个线程对象,并且std::thread
应该抛出一个构造函数std::system_error
.这就是我理解C++ 11与今天的操作系统相结合的方式.如果有一个具有线程支持的操作系统,它不会在其进程中产生主线程,请告诉我.
如果需要保持对线程的控制以便正确关闭,可以通过使用同步原语和/或某种标志来实现.但是,在这种情况下,设置关闭标志后跟连接是我喜欢的方式,因为通过分离线程没有必要增加复杂性,因为无论如何资源都会被释放,其中std::thread
对象的几个字节相对于更高的复杂性和可能更多的同步原语应该是可接受的
Cae*_*sar 17
程序退出后线程的命运是未定义的行为.但是现代操作系统会在关闭它时清理进程创建的所有线程.
在分离时std::thread
,这三个条件将继续保持
*this
不再拥有任何线程 Vin*_*nod 17
请考虑以下代码:
#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;
}
Run Code Online (Sandbox Code Playgroud)
在Linux系统上运行它,从不打印来自thread_fn的消息.操作系统确实会thread_fn()
在main()
退出后立即清理.替换t1.detach()
为t1.join()
始终按预期打印消息.