C ++ 11:如果你没有为std :: thread调用join()会发生什么

时间:2014-12-10 03:13:42

标签: c++ multithreading c++11 stdthread

如下所示:

void test() 
{
  std::chrono::seconds dura( 20 );
  std::this_thread::sleep_for( dura );
}

int main()
{
  std::thread th1(test);
  std::chrono::seconds dura( 5 );
  std::this_thread::sleep_for( dura );
  return 0;
}

main将在5秒后退出,th1仍会执行的操作会怎样?

即使您在th1中定义的main线程对象超出范围并被销毁,它是否会继续执行直到完成?

th1在完成执行后是否只是坐在那里,或者在程序终止时以某种方式得到清理?

如果线程是在函数中创建的,而不是main - 如果线程在程序终止或函数超出范围之前保持不变,该怎么办?

如果你想在线程上有某种类型的超时行为,那么简单地不为线程调用join是否安全?

3 个答案:

答案 0 :(得分:19)

如果在调用析构函数时没有分离或加入线程,它将调用std::terminate,我们可以通过转到draft C++11 standard看到30.3.1.3 线程析构函数说:

  

如果是joinable(),则调用std :: terminate()。否则,没有任何影响。 [   注意:隐式分离或连接joinable()线程   它的析构函数可能导致难以调试正确性(for   分离)或性能(用于加入)仅在遇到错误时才遇到错误   提出异常。因此程序员必须确保   当线程仍然可以连接时,永远不会执行析构函数。 -结束   注意]

至于这种行为的基本原理,我们可以在(Not) using std::thread

中找到一个很好的摘要
  

为什么可连接线程的析构函数必须调用   的std ::终止?毕竟,析构函数可以与孩子一起加入   线程,或者它可以从子线程分离,或者它可以取消   线程。简而言之,你不能像这样加入析构函数   导致意外(未在代码中明确指出)程序   在f2抛出的情况下冻结。

然后是一个例子,并说:

  

你不能分离,因为它会冒主线程的风险   留下子线程启动的范围和子项   线程继续运行并保持对已经范围的引用   走了。

这篇文章引用了N2802: A plea to reconsider detach-on-destruction for thread objects这是反对先前提案的论据,该提案如果可以连接则拆除销毁,它注意到两种替代方案中的一种是加入可能导致死锁的另一种选择是我们拥有的今天,如果可以加入,则会std::terminate进行销毁。

答案 1 :(得分:5)

std::thread::~thread()

  

如果*这有一个关联的线程(joinable() == true),则调用std::terminate()

来源:http://en.cppreference.com/w/cpp/thread/thread/~thread

这意味着像这样的程序根本没有格式或安全。

但请注意,在这种情况下,boost::thread::~thread()会调用detach()。 (作为注释中的用户 dyp ,在最近的版本中不推荐使用此行为)

您始终可以使用RAII解决此问题。只需将您的线程包装在另一个类中,该类将在销毁时具有所需的行为。

答案 2 :(得分:1)

在C ++ 11中,您必须明确指定'会发生什么?'当新创建的线程超出范围时(我们调用它的dtor)。有时,当我们确定主线程正在继续,并且我们的线程正在充当管道时,可以安全地分离()'他们;有时当我们等待我们的WORKER线程完成他们的操作时,我们加入()'它们。

正如this所说,程序员必须确保在线程仍然可以连接时永远不会执行析构函数。

指定您的多线程策略。在此示例中,调用std::terminate()