当前位置: 首页 > news >正文

C++一文讲透thread中的detach和join的差别

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、thread详解
  • 二、线程何时运行
  • 三、线程启动方式
    • 1.join
    • 2.detach
  • 总结


前言

无论哪种语言线程在绝大多数项目中都是会用到的,C++也一样,C++包装了一个std::thread类可以简化程序员的编程,但在使用过程中也要注意区分用法,否则可能适得其反。

今天要讨论的就是单纯的thread的join(等待)和detach(分离)两种情景,让不懂得人看完之后茅塞顿开。


一、thread详解

C++标准库成员,是对线程的包装类,在thread头文件里面,使用方式是std::thread。创建线程本身就是通过创建一个thread类对象然后和系统线程完成绑定(映射),这样可以通过操作thread类对象来管理创建的系统线程,系统线程本身行为受到内核调度机制影响。

示例代码:

#include <iostream>
#include <thread>// 线程函数
void threadFunction() {std::cout << "Thread function is running" << std::endl;
}int main() {// 创建线程并启动std::thread t(threadFunction);// 等待线程完成t.join();std::cout << "Main thread is running" << std::endl;return 0;
}

注意:这个地方如果不使用t.join()可能导致main线程早于子线程结束,引发terminate!

二、线程何时运行

这个问题一定要说清楚才行,我以前用java的时候线程有一个start()可以决定什么时候启动线程,C++则不是这样,C++没有一个明确方法决定线程何时运行,只需要满足创建并等待(一般时间很短,取决于实际情况),直到线程获取了资源之后就会自动运行,而不需要显式调用方法。且无论是以join还是detach方式启动的线程都受到内核的调度。

三、线程启动方式

1.join

主线程(创建子线程的那个线程)等待子线程执行完毕才进行下一步,适合那种需要立刻得到结果的场景。创建方式很简单,你只需要先创建线程然后在合适的时候调用t.join()等待结束即可。

示例代码:

#include <iostream>
#include <thread>int main() {std::thread t([]{std::cout << "Lambda function is running" << std::endl;});t.join();std::cout << "Main thread is running" << std::endl;return 0;
}

当调用t.join()方法时主线程的等待,直到t线程结束主线程才会继续运行,程序结束。如果子线程里面有死锁主线程就会一直等待,永无休止。

2.detach

在C++中,std::thread提供了一个detach方法,可以将线程从其控制对象中分离。分离的线程会在后台独立运行,直到完成。此方法常用于不需要同步或获取线程结果的情况。不过,在使用detach时需要小心,因为主线程不能再控制或管理该分离的线程,这可能会导致资源管理和程序结束时的问题。

另外,detach之后的变量不能重新join。

示例代码:

#include <iostream>
#include <thread>
#include <chrono>void worker() {std::this_thread::sleep_for(std::chrono::seconds(2));std::cout << "Worker thread finished\n";
}int main() {std::thread t(worker);// 分离线程t.detach();std::cout << "Main thread finished\n";// 此处主线程可能已经结束,而分离的线程仍在运行std::this_thread::sleep_for(std::chrono::seconds(3));  // 确保主线程不会立即退出return 0;
}

创建的子线程很快就会结束,所以主线程3秒的休眠足够了,如果子线程超过3秒就会导致主线程早于子线程结束,程序terminate。

terminate called without an active exceptionProcess finished with exit code 3

所以,还是要面对一个问题,detach后的线程怎么管理?我并不能时时刻刻知道线程的状态,如果主线程结束了子线程没结束怎么办?方法还是有的,那就是增加标志位(同步需要atomic或锁),如果我想一个线程结束只需要修改标志位。比如下面的代码:

#include <iostream>
#include <thread>
#include <atomic>
#include <chrono>std::atomic<bool> keepRunning(true); // 原子布尔变量,用于控制线程执行void threadFunction() {while (keepRunning) {std::cout << "Thread is running..." << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));}std::cout << "Thread stopped." << std::endl;
}int main() {std::thread t(threadFunction);// 主线程等待一段时间后停止子线程std::this_thread::sleep_for(std::chrono::seconds(5));keepRunning = false; // 设置标志位,通知线程停止t.join(); // 等待线程结束std::cout << "Main thread is running" << std::endl;return 0;
}

还可以借助boost库的方法来打断线程。例如:

#include <iostream>
#include <boost/thread.hpp>void threadFunction() {try {while (true) {// 执行一些工作boost::this_thread::interruption_point();}} catch (boost::thread_interrupted&) {std::cout << "Thread interrupted." << std::endl;}
}int main() {boost::thread t(threadFunction);// 主线程等待一段时间后中断子线程boost::this_thread::sleep_for(boost::chrono::seconds(5));t.interrupt(); // 中断线程t.join(); // 等待线程结束std::cout << "Main thread is running" << std::endl;return 0;
}

切记,不要随意中断线程,防止资源泄露!不管是标志位、条件判断还是boost库都必须考虑资源回收的问题,否则如果子线程里面使用了自由存储将永远无法得到回收。


总结

不管哪种方式启动的线程都要保证资源的回收,特别是当心主线程早于子线程结束的情况。还有一种容易让人疏忽的情况:如果你在一个函数里创建了thread对象,你没有join或detach,如果在函数return前线程没被回收就会导致terminate。比如,下面的代码:

void test(){
}void thread_join_detach(){std::thread t(test);
//    t.detach();
//    t.join();
}int main() {thread_join_detach();return 0;
}

上面的代码thread_join_detach函数返回会回收t对象,如果线程t还没结束就会terminate。解决方法有很多,比较简单的就是在函数return之前调用join或detach,还有就是把t提升作用域,比如全局变量。

http://www.lryc.cn/news/379622.html

相关文章:

  • 当Windows台式电脑或笔记本电脑随机关机时,请先从这8个方面检查
  • 【凤凰房产-注册安全分析报告-缺少轨迹的滑动条】
  • 【建议收藏】逻辑回归面试题,机器学习干货、重点。
  • C++使用教程
  • k8s volcano + deepspeed多机训练 + RDMA ROCE+ 用户权限安全方案【建议收藏】
  • 设计模式(七)创建者模式之建造者模式
  • # class中的__call__方法解析
  • React逻辑复用的方式都有哪些
  • 【LinuxC语言】线程重入
  • 【Streamlit学习笔记】Streamlit-ECharts箱型图添加均值和最值label
  • Docker镜像仓库:存储与分发Docker镜像的中央仓库
  • FreeRTOS必考面试题及参考答案
  • 面试题2:从浏览器输入一个URL,到最终展示前端页面这一过程,会发生什么?
  • <Rust><iced><resvg>基于rust使用iced构建GUI实例:使用resvg库实现svg转png
  • 面试突击:Java 中的泛型
  • 3_2、MFC常用控件用法:组合框、滚动条和图片控件
  • 如何使用gprof对程序进行性能分析
  • 四川汇聚荣科技有限公司靠谱吗?
  • 可灵王炸更新,图生视频、视频续写,最长可达3分钟!Runway 不香了 ...
  • oracle中使用临时表GLOBAL TEMPORARY TABLE
  • Gradio入门—快速开始
  • AOP应用之系统操作日志
  • 海外云手机自动化管理,高效省力解决方案
  • 后仿真中的 《specify/endspecify block》之(5)使用specify进行时序仿真
  • win10/11磁盘管理
  • 【昇思初学入门】第四天打卡
  • 禁用/屏蔽 Chrome 默认快捷键
  • 移动端+PC端应用模式的智慧城管综合执法办案平台源码,案件在线办理、当事人信用管理、文书电子送达、沿街店铺分析
  • AI音乐大模型时代:版权归属与创意产业的新生长点
  • C++函数作为参数