为什么需要显式分离短期变量?

时间:2018-07-23 14:04:02

标签: c++ multithreading std stdthread

假设我有一个小操作,我想在一个单独的线程中执行。我不需要知道它什么时候完成,也不需要等待它的完成,但是我不希望该操作阻塞我的当前线程。当我编写以下代码时,会崩溃:

void myFunction() {
    // do other stuff
    std::thread([]()
    {
        // do thread stuff
    });
}

此崩溃可以通过将线程分配给变量并将其分离来解决:

void myFunction() {
    // do other stuff
    std::thread t([]()
    {
        // do thread stuff
    });
    t.detach();
}

为什么必须执行此步骤?还是有更好的方法来创建小的一次性线程?

4 个答案:

答案 0 :(得分:3)

Because the std::thread::~thread() specification says so

  

之后的线程对象没有关联的线程(并且可以安全地销毁)

     
      
  • 它是默认构造的
  •   
  • 它从...移走了
  •   
  • join()已被调用
  •   
  • detach()被称为
  •   

除非您想将线程对象(通过移动)返回给调用者,否则看来detach()是其中唯一有意义的一个。

  

为什么必须执行此步骤?

考虑线程对象代表执行的长期“线程”(轻量级进程或内核可调度实体或类似对象)。

允许您在线程仍在执行时销毁对象,从而使您无法随后加入(并找到该线程的结果)。该 可能是一个逻辑错误,但即使正确退出程序,也可能导致困难。

  

还是有更好的方法来创建小的一次性线程?

显然,但是使用线程池在后台运行任务通常要好得多,而不是启动和停止大量短时线程。

您也许可以改用std::async(),但是如果您尝试丢弃future,则在某些情况下它会在析构函数中返回 may 块。

答案 1 :(得分:0)

请参见the documentation of the destructor of std:thread

  

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

您应该明确地说,您不在乎线程将要发生什么,并且可以放开对该线程的任何控制。这就是detach的目的。

通常,这看起来像一个设计问题,因此崩溃很有意义:很难就这种情况下应该发生的事情提出一个通用且毫不奇怪的规则(例如,您的程序通常也可以终止其执行-应该发生的情况)与线程?)。

答案 2 :(得分:0)

考虑一下:线程A创建线程B,线程A离开其执行范围。线程B的句柄将丢失。现在应该怎么办?有几种可能性,最明显的是:

  1. 线程B被分离并独立地继续执行
  2. 线程A在退出其自身作用域之前等待(加入)线程B

现在您可以争论哪个更好:1或2?我们(编译器)应如何决定哪一种更好?

因此,设计师所做的工作与众不同:崩溃终止代码,以便开发人员明确选择其中一种解决方案。为了避免隐式(也许是不需要的)行为。这是对您的信号:“嘿,现在注意,这段代码很重要,我(编译器)不想为您决定”。

答案 3 :(得分:0)

基本上,您的用例需要调用detach(),因为您的用例非常奇怪,而不是C ++试图简化的事情。

虽然Java和.Net巧妙地让您抛弃了一个其关联线程仍在运行的Thread对象,但是在C ++模型中,Thread更接近于 线程在某种意义上说Thread对象的存在与其所引用的执行的生存期(或至少是可连接性)一致。请注意,如果不启动Thread就不可能创建它(除非是默认构造函数,实际上是在移动语义服务中),或者复制它或从线程中创建一个ID。 C ++希望Thread使线程寿命更长。

保持这种状况有多种好处。操作系统不必自动对线程的控制数据进行最后的清理,因为一旦Thread消失了,任何人都无法尝试加入它。由于主线程是最后一个退出的线程(除非存在一些移动的恶作剧),因此更容易确保及时删除带有线程存储的变量。缺少的join(这是极其的常见错误类型)在运行时会被正确标记。

与之相反,

允许一些线程徘徊在远方,这是 ,但这是不寻常的事情。除非它通过同步对象与您的其他线程进行交互,否则无法确保它完成了原本打算做的事情。分离的线程位于reinterpret_cast级别:您可以告诉编译器您知道它不知道的东西,但这必须是明确的,而不仅仅是您 didn函数的结果't 通话。