171

有时我必须使用它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
}
4

6 回答 6

201

在 的析构函数中std::threadstd::terminate如果:

  • 线程未加入(使用t.join()
  • 并且也没有分离(用t.detach()

因此,在执行流到达析构函数之前,您应该始终选择一个join或一个线程。detach


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

至关重要的是,这意味着这些线程的堆栈不会展开,因此某些析构函数不会被执行。根据这些析构函数应该执行的操作,这可能与程序崩溃或被杀死一样糟糕。希望操作系统会释放对文件等的锁定……但您可能已经损坏了共享内存、半写文件等。


那么,您应该使用joinordetach吗?

  • 采用join
  • 除非您需要更大的灵活性并且愿意提供同步机制来自己等待线程完成在这种情况下您可以使用detach
于 2014-04-02T07:50:04.407 回答
41

detach如果您不打算等待线程完成,则应该调用,join但线程将继续运行直到完成,然后终止而无需生成器线程专门等待它;例如

std::thread(func).detach(); // It's done when it's done

detach基本上会释放需要能够执行的资源join

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

于 2014-04-02T06:54:46.250 回答
21

此答案旨在回答标题中的问题,而不是解释 和 之间的join区别detach。那么应该什么时候std::thread::detach使用呢?

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

然而,一些应用程序依赖于旧的且通常设计和支持不佳的 API,这些 API 可能包含无限期的阻塞函数。将这些函数的调用移动到专用线程中以避免阻塞其他东西是一种常见的做法。没有办法让这样的线程优雅地退出,所以使用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;
}
于 2019-10-03T15:16:14.637 回答
16

join()当您分离线程时,这意味着您在退出之前不必这样做main()

线程库实际上会在 main 下等待每个这样的线程,但你不应该关心它。

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

于 2014-04-02T07:01:17.757 回答
3

根据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结束了,新线程也不会发生任何事情。

于 2018-07-17T09:32:14.957 回答
0

迭代上述答案之一中提到的内容也许是个好主意:当主函数完成并且主线程正在关闭时,所有生成线程都将被终止或挂起。因此,如果您在主线程关闭后依靠 detach 让后台线程继续运行,您会大吃一惊。要查看效果,请尝试以下操作。如果您取消注释最后一个睡眠调用,则将创建输出文件并将其写入正常。否则不是:

#include <mutex>
#include <thread>
#include <iostream>
#include <fstream>
#include <array>
#include <chrono>

using Ms = std::chrono::milliseconds;

std::once_flag oflag;
std::mutex mx;
std::mutex printMx;
int globalCount{};
std::ofstream *logfile;
void do_one_time_task() {
    //printMx.lock();
    //std::cout<<"I am in thread with thread id: "<< std::this_thread::get_id() << std::endl;
    //printMx.unlock();
    std::call_once(oflag, [&]() {
    //  std::cout << "Called once by thread: " << std::this_thread::get_id() << std::endl; 
    //  std::cout<<"Initialized globalCount to 3\n";
        globalCount = 3;
        logfile = new std::ofstream("testlog.txt");
        //logfile.open("testlog.txt");
        });
    std::this_thread::sleep_for(Ms(100));
    // some more here
    for(int i=0; i<10; ++i){    
        mx.lock();
        ++globalCount;
        *logfile << "thread: "<< std::this_thread::get_id() <<", globalCount = " << globalCount << std::endl;
        std::this_thread::sleep_for(Ms(50));
        mx.unlock();
        std::this_thread::sleep_for(Ms(2));
    }

    std::this_thread::sleep_for(Ms(2000));
    std::call_once(oflag, [&]() {
        //std::cout << "Called once by thread: " << std::this_thread::get_id() << std::endl;
        //std::cout << "closing logfile:\n";
        logfile->close();
        });

}

int main()
{
    std::array<std::thread, 5> thArray;
    for (int i = 0; i < 5; ++i)
        thArray[i] = std::thread(do_one_time_task);

    for (int i = 0; i < 5; ++i)
        thArray[i].detach();

    //std::this_thread::sleep_for(Ms(5000));
    std::cout << "Main: globalCount = " << globalCount << std::endl;

    return 0;
}
于 2021-10-30T19:50:22.970 回答