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

du7egjpx  于 2023-03-25  发布在  其他
关注(0)|答案(6)|浏览(234)

有时候我不得不使用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
}
14ifxucb

14ifxucb1#

std::thread的析构函数中,如果:

  • 线程未连接(使用t.join()
  • 也未分离(使用t.detach()

因此,在执行流到达析构函数之前,您应该总是joindetach线程。
当一个程序终止时(即main返回),其余在后台执行的分离线程不会被等待;而是挂起它们的执行,并且不析构它们的线程局部对象。
关键的是,这意味着 * 这些线程的堆栈没有被展开 *,因此一些析构函数没有被执行。根据这些析构函数应该执行的操作,这可能是一个糟糕的情况,就像程序崩溃或被杀死一样。希望操作系统会释放对文件的锁定等...但你可能已经损坏了共享内存,半写文件等。
那么,应该使用join还是detach呢?

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

kh212irz2#

如果你不打算等待线程完成join,但线程会一直运行,直到它完成,然后在没有spawner线程等待它的情况下终止,那么你应该调用detach;例如:

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

detach基本上将释放实现join所需的资源。
如果一个线程对象结束了它的生命,并且joindetach都没有被调用,这是一个致命错误;在这种情况下,调用terminate

ee7vknir

ee7vknir3#

这个答案是为了回答标题中的问题,而不是解释joindetach之间的区别。那么什么时候应该使用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;
}
ohfgkhjo

ohfgkhjo4#

当你分离线程时,这意味着你在退出main()之前不必join()它。

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

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

s8vozzvw

s8vozzvw5#

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

wkftcu5l

wkftcu5l6#

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

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

相关问题