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

Linux线程(3)线程控制

本章目标

1.创建线程
2.线程终止
3.线程等待
4.线程分离

1.创建线程

在Linux当中posix线程库当中提供了我们有关于线程的一系列的接口,但是这些接口都是基于Linux环境下设计的,以LWP为主的.它是不提供跨平台性的.但是市面上99%的服务器上都运行着Linux的环境的服务.如果我们想在window或者是mac上部署服务的话,我们可以使用c++提供给我们的并发库的接口去使用.,c++11的并发库的底层是封装了其他平台的系统调用做出来的.一套代码可以在不同的平台上跑.有关于c++11并发库的东西,我们会在后续进行介绍.但是今天,我们只考虑Linux当中给我们提供的接口.

在Linux当中我们创建线程所使用的接口是pthread_create,它一共有四个参数.
1.第一个参数是pthread_t 类型的,它就是我们外部提供的tid,跟我们的进程的pid是向对应的

它也是一个整数类型,不过它是被typedef出来的.它是用来标识一个线程的唯一标识.
因为它并没有在内核当中维护.tcb这个结构,而是在用户栈当中的共享区去进行维护的.所以我们要在外部进行定义这个变量,方便,我们后续对线程进行等待,分离.
2.第二个参数是线程的属性.我们一般如果是只在线程内部使用,没有任何其他的定制化需求的,我们一般是选择传nullptr,让它创建线程的时候选择默认的创建行为.
3.第三个就是我们线程的入口函数.它是一格void*(ptr)(void)类型的函数指针.它用来指向,我们函数的的入口函数.但是我们可以从这个地方看出一点问题.我们这个线程的参数,在这里只有一个参数的位置.因为c并没有提供向可变模板参数,它自己的可变参数实现出来,由十分的复杂,我们在传多参数的时候,往往是把数据打包成一格类或者说是结构体的方式传进去,然后在内部在强转回来.而且定义这个结构体变量,我们一般是在堆区进行开辟的.因为这个数据是属于我们要交给子线程.它和主线程属于不同的执行流.我们为了防止栈上变量在主线程当中出作用域直接自动销毁.我们要传一个在堆上的空间.在子进程内部使用完之后再对这块区域进行free或者delete.在这里我们有关于这个地方入口函数的返回值,我们放到进程等待的地方去说
第四个参数就是我们要传进去的参数,的指针.如果我们不需要传参数的话,我们一般是给nullptr.

#include<iostream>#include<unistd.h>#include<pthread.h>structdata{inta;intb;};void*hander(void*arg){data*d=(data*)arg;std::cout<<d->a<<d->b<<std::endl;deleted;returnnullptr;}intmain(){pthread_t tid;data*d=newdata{1,2};pthread_create(&tid,nullptr,hander,(void*)d);sleep(100);return0;}


我们是能够看到结果的,在这里我们一定要在主线程不让它走到return的地方.因为线程的创建也是需要时间的.我们要给他们时间去创建线程,同时让线程执行完自己的任务.

2.线程终止

在Linux当中线程既然能够进行创建就一定能够进行运行,我们线程也可以进行终止.这个终止并不是线程自己主动的去结束.而是当线程执行到一半的时候我们线程自己能主动的去终止线程.或者说是主线程去终止线程
在Linux当中,给我们提供下面这些接口去实现线程的终止

这个只有一格参数需要我们传进去对应线程的tid,这个就是我们线程自己的创建时候,我们传进去的第一个参数.但是对于子线程当中,它由如何拿到自己线程的tid呢?
在Linux当中给我们提供相应的函数.

pthread_self,它的参数没有.它的返回值就是当前所处线程的pid.

#include<iostream>#include<unistd.h>#include<pthread.h>structdata{inta;intb;};void*hander(void*arg){data*d=(data*)arg;std::cout<<d->a<<d->b<<std::endl;deleted;sleep(100);returnnullptr;}intmain(){pthread_t tid;data*d=newdata{1,2};pthread_create(&tid,nullptr,hander,(void*)d);sleep(5);pthread_cancel(tid);sleep(100);return0;}

我呢吧在这里在5秒后去取消这个我们创建的子线程,我们可以再另起一个终端去查看效果

我们在左边能够看到这个效果,最开始只有两个线程,但是有一个线程最后被取消了.
如果我们想在子线程当中自己取消自己,我们可以在合适的时机这么写

pthread_cancel(pthread_self());

即可,这里很简单,我们就不重复进行实验了,二者的效果实际上是一样的.

对于线程取消,它的返回值,如果说是被成功终止取消了,那么它就会返回0,如果失败了就会返回一个非0的值

3.线程等待

我们在进程部分,我们进程退出的时候要对其子进程进行等待回收.如果我们不回收就会出现僵尸进程的问题.如果我们主进程长期不退出的话,就会出现非常严重的内存泄漏的问题.我们在过去有很多解决方法.除了老老实实的进行回收.还有去捕捉SIGCHILD这个信号.在这个信号捕捉的自定义函数内部实现一格非轮询等待的方式去进行回收.或者说是通过singal去忽略SIGCHILD这个信号的方式,让主进程自己去回收,也还有创建孙子进程,把实现逻辑放到孙子当中.让孙子变为孤儿进程,最后让操作系统统一进行回收.方式有很多.
对于线程部分,在上面我们看到如果我们线程结束了,我们LWP确实是被内核新型回收了.但是实际上我们的线程也会出现类似于"僵尸进程"的问题.在上面进程创建,我们说过线程的入口函数会有一个返回值.我们如果有需要可以将子线程完成任务的情况.进行返回.但是这个返回结果我们要放到哪里?
肯定不能够是LWP当中.这个东西一旦结束就会被回收.而是放到我们在上一格小节所说的TCB当中的.这个TCB是在整个进程地址空间当中的共享区当中进行维护的.也就是说它是被我们的posix原生线程库所管理起来的.
我们即使不想要拿到这个退出信息,也要对这块空间进行回收.所以在Linux当中给我们提供了回收子进程的接口

这个函数有两个参数,第一个就是我们回收线程的tid,二哥就是我们的返回值.它是一个void** 类型.因为我们返回来的类型是一格void* 类型的,对于指针类型来说,如果作为传进去,想要作为输出型参数带出来.它就需要升为二级指针.我们在这里同样可以定义一格结构表示我们的进程完成的结果.


对于线程等待的返回值,我们如果成功返回会返回一个0,失败就会设置一格错误码,一般大于0

#include<iostream>#include<unistd.h>#include<pthread.h>structdata{inta;intb;};classreturnval{public:inttruecode;intresult;};void*hander(void*arg){data*d=(data*)arg;std::cout<<d->a<<d->b<<std::endl;returnval*ret=newreturnval();ret->truecode=1;ret->result=d->a+d->b;deleted;return(void*)ret;}intmain(){pthread_t tid;data*d=newdata{1,2};pthread_create(&tid,nullptr,hander,(void*)d);sleep(5);void*ret=nullptr;pthread_join(tid,&ret);std::cout<<"truecode is "<<((returnval*)ret)->truecode<<" result is "<<((returnval*)ret)->result<<std::endl;delete(returnval*)ret;sleep(100);return0;}


我们在子线程完成计算,然后通过子线程的带出计算结果,当线程成功回收,我们也就能够拿到这一块的退出信息了.

4.线程分离

如果我们不想对线程进行回收,我们不关系线程的返回值,我们想让它完成自己的任务后主动退出
我们可以将线程设置为分离状态.我们就不要进行回收了
• 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进⾏pthread_join操作,否则
⽆法释放资源,从⽽造成系统泄漏。
• 如果不关⼼线程的返回值,join是⼀种负担,这个时候,我们可以告诉系统,当线程退出时,⾃
动释放线程资源。

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<pthread.h>void*thread_run(void*arg){pthread_detach(pthread_self());printf("%s\n",(char*)arg);returnNULL;}intmain(void){pthread_t tid;if(pthread_create(&tid,NULL,thread_run,(void*)("thread1 run..."))!=0){printf("create thread error\n");return1;}intret=0;sleep(1);//很重要,要让线程先分离,再等待if(pthread_join(tid,NULL)==0){printf("pthread wait success\n");ret=0;}else{printf("pthread wait failed\n");ret=1;}returnret;}

http://www.jsqmd.com/news/454024/

相关文章:

  • 探寻2026年安徽口碑好的AI搜索推广专业公司,价格怎么收费 - 工业推荐榜
  • 永磁同步电机无位置观测算法:实测有效的宝藏秘籍
  • 【RabbitMQ】超详细Windows系统下RabbitMQ的安装配置
  • 2026年深圳配眼镜品牌全、服务好的配镜中心排名大揭秘 - mypinpai
  • 2026年工业移动电源厂家推荐排行榜:大功率/220V/380V/便携式/应急/工程施工/大容量/快充/消防救援/户外/储能式移动电源,专业实力与创新技术深度解析 - 品牌企业推荐师(官方)
  • 计算机毕设java的高校车辆租赁管理系统 基于SpringBoot框架的校园汽车共享与调度服务平台 Java技术驱动的高校公务车辆与共享出行一体化管理系统
  • Simulink车用永磁同步电机弱磁控制的矢量控制FOC
  • 2026年腾讯企业邮箱开通服务商怎么选:资质、价格与服务对比详解 - 品牌2026
  • 镀锌板水箱选购指南:核心要素解析+Top5厂家推荐,工业与市政项目必看 - 深度智识库
  • 【MySQL统计函数count详解】
  • OpenGait(步态识别框架)的配置项说明
  • 2026年3月建筑AI深度测评报告:6款工具对比,EVAI第一
  • 飞跨电容型NPC逆变器仿真(SPWM) 仿真包含FCNPC拓扑、LCL滤波器、三相纯阻性负载构...
  • 2026年靠谱的文审机_娃娃机_弹珠机源头厂家口碑推荐-陕西英杰儿童主题乐园有限公司 - 朴素的承诺
  • 一次生产事故,我用AI分析日志找到了根因
  • Postman接口测试与自动化实战
  • 自然语言处理 —— 基础入门
  • 【python】pyspark.errors.exceptions.base.PySparkRuntimeError [JAVA_GATEWAY_EXITED] Java gateway proce
  • [Maui] 造轮子——LoggerSqlite
  • 论文写作必备:专业 AI 软件排名与选择指南
  • 基于DC 12kV、N=12的多电平MMC仿真研究:最近电平逼近调制与排序均压策略实现
  • llc半桥变换器多种控制方式仿真模型:变频、PWM、滞环、自抗扰等控制方式的半桥llc谐振变换...
  • 面对选型困惑与安装难题,如何与可靠的钢格板厂家高效合作? - 企师傅推荐官
  • 中心碰撞优化算法(CCO)-2026年SCI一区新算法-公式原理详解与性能测评 Matlab代码免费获取
  • win11本地部署openclaw实操第9集-配置 OpenClaw 连接本地模型参数
  • 20260309学习 - 小镇
  • QT聊天项目(11)
  • LITESTAR 4D应用:道路附加模块
  • 合规必看|2026西安文审机厂家实力测评,陕西英杰解锁全链条服务 - 朴素的承诺
  • 双碳+24小时分时综合能源系统低碳优化调度:Matlab+Yalmip+Cplex实现综合元素...