我应该在什么时候使用std::thread::detach
When should I use std::thread::detach?
有时我不得不使用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
}
在std::thread
的析构函数中,如果:,则调用std::terminate
- 线程未连接(使用
t.join()
( - 并且也没有分离(用
t.detach()
(
因此,在执行流到达析构函数之前,应该始终将join
或detach
作为线程。
当程序终止时(即main
返回(,不等待在后台执行的剩余分离线程;相反,它们的执行被挂起,并且它们的线程本地对象不被破坏。
至关重要的是,这意味着这些线程的堆栈没有展开,因此一些析构函数没有执行。根据这些析构函数应该采取的行动,这可能会像程序崩溃或被杀死一样糟糕。希望操作系统能释放对文件等的锁定……但你可能已经损坏了共享内存、半写文件等。
那么,您应该使用join
还是detach
?
- 使用
join
- 除非您需要更大的灵活性,并且愿意提供一种同步机制来等待线程完成,否则,在这种情况下,您可以使用
detach
如果您不打算等待线程完成join
,那么您应该调用detach
,但线程将一直运行直到完成,然后终止,而不需要spawner线程专门等待它;例如
std::thread(func).detach(); // It's done when it's done
CCD_ 18基本上将释放能够实现CCD_ 19所需的资源。
如果线程对象结束了它的生命,并且join
和detach
都没有被调用,这是一个致命的错误;在这种情况下调用CCD_ 22。
此答案旨在回答标题中的问题,而不是解释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;
}
当您分离线程时,这意味着您在退出main()
之前不必join()
。
线程库实际上会等待main下方的每个这样的线程,但您不应该关心它。
当您有一个必须在后台完成的任务,但您不关心它的执行时,detach()
主要很有用。这通常是一些库的情况。他们可能会默默地创建一个后台工作线程并将其分离,这样你甚至不会注意到它
将执行线程与线程对象分离,允许独立执行。任何分配的资源都将线程退出后释放。
调用分离后,
*this
不再拥有任何线程。
例如:
std::thread my_thread([&](){XXXX});
my_thread.detach();
注意局部变量:my_thread
,当my_thread
的生存期结束时,将调用std::thread
的析构函数,并在析构函数内调用std::terminate()
。
但是,如果使用detach()
,就不应该再使用my_thread
,即使my_thread
的生命周期结束,新线程也不会发生任何事情。
也许最好迭代上面一个答案中提到的内容:当主函数完成并且主线程关闭时,所有派生线程都将被终止或挂起。因此,如果您在主线程关闭后依靠分离来让后台线程继续运行,那么您会大吃一惊。要查看效果,请尝试以下操作。如果取消对最后一个睡眠调用的注释,那么将创建输出文件并将其写入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 3n";
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;
}
- 在std::thread中,joinable()然后join()线程安全吗
- <Windows>为什么 std::thread::native_handle 返回类型为"long long unsigned int"的值,而不是 void*(又名 HANDLE)?
- 分离一个静态常量 std::thread?
- 尝试使用 std::vector<std::thread时出现静态断言失败错误>
- 使用 thread 类在 C++ 中构造线程的动态数组时出错
- 当指向对象的指针作为参数传递给 std::thread 时,内存可见性
- 如何从 std::thread 返回值
- 在C++中使用并行化的预期速度是多少(不是 OpenMp,而是 <thread>)
- 将 std::thread by 值推送到列表中
- 转发变量参数列表以模拟 std::thread
- 嵌入式设备 -> std::thread -> FreeRTOS?
- 对 'std::thread::_M_start_thread CMake 的未定义引用进行基准测试
- std::thread 增加 DLL 引用计数,从而防止卸载 DLL
- 如何防止 std::thread 在 QT 中冻结 GUI?
- 对带有唯一指针的 std::thread 使用类成员函数时出现编译错误
- 线程在销毁包含该线程的对象时调用 thread.detach()
- thread.detach() 函数的左值何时会超出以下代码的范围
- 当调用std :: thread.detach时出乎意料的行为
- 我应该在什么时候使用std::thread::detach
- c++ 11 std::thread::detach和访问共享数据