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

问题描述 投票:0回答:6

有时我不得不使用

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
}
c++ c++11 stdthread
6个回答
221
投票

std::thread
的析构函数中,
std::terminate
被调用如果:

  • 线程没有连接(用
    t.join()
  • 也没有分离(与
    t.detach()

因此,在执行流到达析构函数之前,您应该总是

join
detach
一个线程。


当程序终止(即

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

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


那么,你应该使用

join
还是
detach

  • 使用
    join
  • 除非你需要有更大的灵活性并且愿意提供一个同步机制来等待线程完成自己,在这种情况下你可以使用
    detach

47
投票

如果您不打算等待线程完成

detach
,您应该调用
join
但是线程将继续运行直到它完成然后终止而没有spawner线程专门等待它;例如

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

detach
基本上将释放能够实施
join
.

所需的资源

如果一个线程对象结束了它的生命并且

join
detach
都没有被调用,这是一个致命错误;在这种情况下
terminate
被调用。


33
投票

本回答旨在回答标题中的问题,而不是解释

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;
}

16
投票

当你分离线程时,这意味着你在退出之前不必

join()
main()
.

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

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


2
投票

根据cppreference.com

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

调用分离后

*this
不再拥有任何线程。

例如:

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

注意局部变量:

my_thread
,当
my_thread
的生命周期结束时,会调用
std::thread
的析构函数,并且会在析构函数内部调用
std::terminate()

但是如果你用了

detach()
,你就不应该再用
my_thread
了,即使
my_thread
的生命周期结束了,新的线程也不会发生任何事情。


1
投票

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

#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;
}
© www.soinside.com 2019 - 2024. All rights reserved.