我什么时候应该使用std :: thread :: detach?


140

有时我必须使用它std::thread来加速我的应用程序。我也知道join()等到线程完成。这很容易理解,但是调用detach()和不调用有什么区别?

我以为如果没有detach(),线程的方法将独立使用线程工作。

不分离:

void Someclass::Somefunction() {
    //...

    std::thread t([ ] {
        printf("thread called without detach");
    });

    //some code here
}

调用与分离:

void Someclass::Somefunction() {
    //...

    std::thread t([ ] {
        printf("thread called with detach");
    });

    t.detach();

    //some code here
}


无论stdboost线程都detachjoinPOSIX线程之后蓝本。
n。代词

Answers:


149

在的析构函数中std::threadstd::terminate如果发生以下情况,则称为:

  • 线程未加入(带有t.join()
  • 并且也未与(分离t.detach()

因此,你应该总是要么join还是detach一个线程执行的流之前到达析构函数。


当程序终止(即main返回)时,不会等待在后台执行的其余分离线程。相反,它们的执行被挂起,并且它们的线程本地对象被破坏。

至关重要的是,这意味着不会解开那些线程的堆栈,因此不会执行某些析构函数。根据那些破坏者应该采取的行动,情况可能像程序崩溃或被杀死一样糟糕。希望操作系统将释放文件等上的锁,但是您可能损坏了共享内存,半写文件等。


因此,您应该使用join还是detach

  • join
  • 除非您需要更大的灵活性并且愿意提供一种同步机制来独自等待线程完成,否则您可以使用detach

如果我打电话给pthread_exit(NULL); 在main()中,则不会从main()中调用exit(),因此程序将继续执行,直到所有分离的线程完成为止。然后将调用exit()。
萨瑟顿

1
@Matthieu,为什么我们不能加入std :: thread的析构函数?
约翰·史密斯,2016年

2
@johnsmith:一个好问题!加入后会发生什么?您等待直到线程完成。如果引发了异常,则将执行析构函数……然后突然中止异常的传播,直到线程终止。不这样做的原因有很多,特别是它是否正在等待当前挂起的线程的输入!因此,设计师选择了一个明确的选择,而不是选择一个有争议的默认设置。
Matthieu M.

@Matthieu我认为您的意思是在std :: thread的析构函数到达之前调用join()。您可以(并且应该吗?)join()封闭类的析构函数?
Jose Quinteiro '16

4
@JoseQuinteiro:实际上,与其他资源不同,建议不要从析构函数中加入。问题在于联接并不会结束线程,它只是等待线程结束,并且与您有适当的信号导致线程终止的信号不同,您可能要等待很长时间... 阻塞当前线程的堆栈正在解绕并防止当前线程终止,从而阻止了线程等待它,等等。因此,除非您确定可以在合理的时间内停止给定线程,否则最好不要等待它在一个析构函数中。
Matthieu M.

25

detach如果您不打算等待线程完成,则应调用,join而线程将一直运行直到完成,然后终止而不必让主线程专门等待它。

detach基本上将释放能够实施所需的资源join

这是一个致命的错误,如果一个线程对象结束其生命,并没有join,也不detach曾被称为; 在这种情况下terminate被调用。


12
您应该提到,如果线程既未连接未分离,则在析构函数中调用终止
2014年

11

当分离线程时,意味着join()在退出之前不需要线程main()

线程库实际上将等待main- 下面的每个这样的线程,但是您不必理会它。

detach()当您有一项必须在后台完成的任务,但您不关心它的执行时,它主要有用。对于某些库,通常是这种情况。他们可能会默默地创建一个后台工作线程并将其分离,因此您甚至不会注意到它。


这不能回答问题。答案基本上是“分离时分离”。
rubenvb

7

这个答案的目的是在标题答题,而不是解释之间的差异joindetach。那么什么时候应该std::thread::detach使用?

在正确维护的C ++代码中std::thread::detach,根本不应使用。程序员必须确保所有创建的线程正常退出以释放所有获取的资源并执行其他必要的清理操作。这意味着通过调用放弃线程所有权detach不是一种选择,因此join应在所有情况下使用。

但是,某些应用程序依赖于可能包含无限阻塞功能的旧的且通常设计不完善且受支持的API。将这些函数的调用移到专用线程中以避免阻塞其他内容是一种常见的做法。无法使此类线程正常退出,因此使用of join只会导致主线程阻塞。在这种情况下,使用detach代替thread动态分配存储对象分配对象然后有意地泄漏它是一种不太邪恶的选择。

#include <LegacyApi.hpp>
#include <thread>

auto LegacyApiThreadEntry(void)
{
    auto result{NastyBlockingFunction()};
    // do something...
}

int main()
{
    ::std::thread legacy_api_thread{&LegacyApiThreadEntry};
    // do something...
    legacy_api_thread.detach();
    return 0;
}

1

根据cppreference.com

将执行线程与线程对象分开,允许执行独立继续。线程退出后,将释放所有分配的资源。

调用detach之后,*this不再拥有任何线程。

例如:

  std::thread my_thread([&](){XXXX});
  my_thread.detach();

注意局部变量:my_thread,当的生命周期my_thread结束时,的析构函数std::thread将被调用,并将std::terminate()在析构函数内被调用。

但是,如果您使用detach(),则不应再使用my_thread,即使的生命周期my_thread结束,新线程也不会发生任何事情。


好吧,我拿回我刚才说的话。@ TobySpeight
DinoStray,

1
请注意,如果&在lambda捕获中使用,则使用的是按引用包含作用域的变量-因此,最好确保所引用的变量的生存期都比线程的生存期长。
DavidJ
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.