c++线程概述
1.概览
- 线程创建之后,必须使用join待其结束,否则资源无法回收,变成僵尸线程占据资源;当然也可以设置成detach方式,让线程自动回收资源
- 进程也是一样的操作,子进程并不会自动回收资源,是需要手动回收,这样的目的是因为进程之间往往有父子关系,涉及到流程上的同步
2. 正文
2.1 线程中为什么有join?
thread::join()的一个作用就是阻塞直到线程执行完;
线程的使用源于异步并发的需求,如下需要把taskA()和taskB()的计算结果加起来,但假设它们都是耗时的任务,可以通过线程的方式同时跑两个任务;
taskB()运行结束后,threadTask.join()的作用是阻塞等待直到线程的taskA执行完毕,都执行完才能将两者的结果相加;
#include<thread>
int taskAResult = 0;
int taskBResult = 0;
void taskA(){
taskAResult = 1 + 2;
}
void taskB(){
taskBResult = 3 + 4;
}
int main(){
std::thread threadTask(taskA);
taskB();
threadTask.join();
int final = taskAResult + taskBResult;
retrun 0;
}
- 如果使用场景和上面不同,创建线程后是否可以不join?
答案是不行, 出于使用多线程大多数会涉及到数据和流程上的关联,std::thread设计就显示要求了thread对象一定要join,如果不join,在c++thread 对象析构时,会主动raise abort()导致崩溃
~thread(){
if(joinable()) //线程仍然可以join但是却析构了
std::terminate();
}
- join()是否有其它作用?
有,举linux而言,linux是通过创建进程的方式创建线程(Light Weight Process),只不过线程共享了进程的空间,所以除了内存等资源之外,创建的线程会占用进程表(process table),如果不使用join(),线程的资源将无法回收,产生僵尸线程, 系统资源被占用积累多了,会运行卡顿,且无法再创建新的线程。
僵尸线程目前在linux上貌似无法在bash上简单的查到,不知道如何排查, todo
2.2 能否不使用join()?
能,可以通过thread::detach(),将线程设置成detach状态,这样就不用做join()操作,也会自动回收资源;使用detach()后,线程的运行和结束就不可控了,要尤为注意是否访问了别的可能析供的资源(对象,成员,内存等),
#include<thread>
int taskAResult = 0;
void taskA(){
taskAResult = 1 + 2;
}
int main(){
std::thread threadTask(taskA);
threadTask.detach();
retrun 0;
}
2.3 现代c++更好的工具?
c++20中增加了std::jthread, 其通过了RAII 在jthread对象析构的时候会自动调用join
#include <chrono>
#include <iostream>
#include <thread>
#include <utility>
using namespace std::literals;
void f1(int n)
{
for (int i = 0; i < 5; ++i)
{
std::cout << "Thread 1 executing " << n <<std::endl;
++n;
std::this_thread::sleep_for(1s);
}
}
int main()
{
std::jthread t1(f1, 0);
//t1.join()
return 0;
}
可以看到,主线程运行jthread后并没有显示执行join(),但f1还是运行完成了, (当然也可以显示调用,join过的jthread,析构的时候不会产生重复调用join()的异常)
Thread 1 executing 0
Thread 1 executing 1
Thread 1 executing 2
Thread 1 executing 3
Thread 1 executing 4
当然jthread也和thread一样支持detach,jthread还有哪些新东西?
在循环的任务中,通常会使用一个变量让线程能退出循环,而jthread则引入了std::stop_token用来控制线程循环结束
#include <chrono>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <string_view>
#include <thread>
using namespace std::chrono_literals;
int main()
{
auto stop_worker = std::jthread([](std::stop_token stoken)
{
int i = 0;
while(!stoken.stop_requested()){
std::this_thread::sleep_for(10ms);
std::cout << "worker is running: "<< ++i << std::endl;
}
std::cout << "worker is stop\n";
});
std::stop_token stop_token = stop_worker.get_stop_token();
std::this_thread::sleep_for(50ms);
stop_worker.request_stop();
return 0;
}
3. ref
【Linux】线程概念 && 线程控制(接口详谈)&& 僵尸线程的产生和解决: https://blog.csdn.net/Sober_harmonic/article/details/124929230
cppreference std::jthread: https://en.cppreference.com/w/cpp/thread/jthread
cppreference std::stop_token: https://en.cppreference.com/w/cpp/thread/stop_token/stop_requested
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 提示词工程——AI应用必不可少的技术
· 地球OL攻略 —— 某应届生求职总结
· 字符编码:从基础到乱码解决
· SpringCloud带你走进微服务的世界
2020-01-14 webrtc源码分析-信号槽实现