【问题标题】:C++11: What happens if you don't call join() for std::threadC++11:如果你不为 std::thread 调用 join() 会发生什么
【发布时间】:2017-07-11 15:39:48
【问题描述】:

如下:

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 会发生什么?

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

th1 是在它完成执行后简单地坐在那里,还是在程序终止时以某种方式被清理?

如果线程是在函数中创建的,而不是在 main 中创建的 - 线程是否会一直保留到程序终止或函数超出范围?

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

【问题讨论】:

  • 你为什么不试试呢?
  • @Drop 因为那不是了解保证的可靠方法。
  • @dyp 对 Stackoverflow 的询问都不是。通过编译运行这个 5 行的例子,至少他能找到一点线索。只有一个保证来源——一个标准。
  • @Drop “在 Stackoverflow 上询问都不行” 好吧,在我看来,一个好的 SO 答案引用了一个可靠的来源(就像你所做的那样)。然而,甚至标准本身也不是完全可靠的来源,因为它包含缺陷。编译器提供者通常会偏离字面解释,例如合并缺陷报告的建议解决方案。由于 Undefined Behaviour 的存在,我认为通过尝试来了解保证并不是一个好习惯。
  • 我想“你为什么不试试呢?”是一个完全有效的回答,如果它表明 OP 在询问之前已经费心进行一些实验,这个问题将会得到改善。 “我试过了,我的程序终止了,这是预期的吗?”是一个比“如果我这样做会发生什么?”更好的问题。我们不是在谈论吃一些野生的、身份不明的浆果​​,C++11 编译器是免费提供的,运行实验既简单又富有教育意义。

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


【解决方案1】:

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

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

至于这种行为的基本原理,我们可以在(Not) using std::thread 中找到一个很好的总结

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

下面是一个例子,也说:

你不能分离,因为它会冒险主线程的情况 离开子线程启动的范围,以及子线程 线程继续运行并保持对已经存在的范围的引用 走了。

这篇文章引用了N2802: A plea to reconsider detach-on-destruction for thread objects,这是反对先前提议的论点,该提议是在可加入的情况下进行破坏,并指出两种选择之一是加入,这可能导致死锁,另一种选择是我们今天所拥有的如果可加入,std::terminate 将被销毁。

【讨论】:

  • "或者它可以取消线程..." "取消" std::thread 是什么意思?顺便说一句,值得注意的是,C++20 添加了 jthread,它在销毁时加入。
【解决方案2】:

std::thread::~thread()

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

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

这意味着这样的程序根本不是格式正确或安全的。

但请注意,在这种情况下,boost::thread::~thread() 会调用 detach()。 (正如用户 dyp 在 cmets 中所说,这种行为在最近的版本中已被弃用)

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

【讨论】:

【解决方案3】:

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

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

指定您的多线程策略。在这个例子中,std::terminate() 被调用。

【讨论】:

    猜你喜欢
    • 2021-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多