Linux系统编程(五)多线程创建与退出
目录
- 一、基本知识点
- 二、线程的编译
- 三、 线程相关函数
- 1. 线程的创建
- (1)整型的传入与接收
- (2)浮点数的传入与接收
- (3)字符串的传入与接收
- (4)结构体的传入与接收
- 2. 线程的退出
- 3. 线程的等待
- 补充
- (1)返回整型
- (2)返回浮点数
- (3)返回字符串
- (4)返回结构体
- 四、综合举例
一、基本知识点
-
定义
线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。每一个进程都至少包含一个main线程。
相比于进程而言,线程更加轻量级,因为它们共享了进程的地址空间以及其他资源,所以线程之间的切换和通信会更加高效。一个进程可以包含多个线程,这些线程可以并发执行,各自独立完成一些特定的任务,或者共同完成一个复杂的任务。进程里每个线程执行的顺序不一定。主线程结束,则所有线程结束。

-
共享资源保护
由于某一线程与同属一个进程的其他的线程共享进程的资源,如内存空间、文件描述符和其他一些进程相关的属性。所以在多线程编程中,通常会涉及到共享资源保护操作,如互斥锁等以确保线程之间的协调运行,避免资源竞争和数据不一致的问题。即每次对共享资源进行操作时,只能有一个线程操作,其他线程必须等待操作完毕后,才能对其继续操作。
二、线程的编译
Linux 的线程是通过用户级的函数库实现的,一般采用 pthread 线程库实现线程的访问和控制。它用第 3 方 posix 标准的 pthread,具有良好的可移植性。在使用了线程的代码编译的时候要在后面加上 -lpthread。
例如:gcc test.c -o test -lpthread
三、 线程相关函数
头文件:#include <pthread.h>

1. 线程的创建
int pthread_create(pthread_t* thread, pthread_attr_t * attr, void *(*start_routine)(void *), void * arg);
//pthread_t* thread :线程的句柄,用于区分是哪个线程。
//pthread_attr_t * attr :线程的属性,通常为NULL。
//void *(*start_routine)(void *) :线程所执行的函数,该函数形参和返回值都要为void *。
// void * arg :该值用于传递第三个参数函数的形参,如果不传递可以为NULL。
举例:

(1)整型的传入与接收
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void *task(void *arg)
{int *num=(int *)arg;while(1){sleep(1); printf("我是子线程,传入参数为%d\n",*num);}
}int main()
{int num =100;pthread_t thread; //定义线程句柄pthread_create(&thread, NULL, task, (void *)&num); //创建线程,并绑定线程函数。 while(1){sleep(1); printf("我是主线程\n");}
}

(2)浮点数的传入与接收
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void *task(void *arg)
{float*num=(float *)arg;while(1){sleep(1); printf("我是子线程,传入参数为%f\n",*num);}
}int main()
{float num =10.10;pthread_t thread; //定义线程句柄pthread_create(&thread, NULL, task, (void *)&num); //创建线程,并绑定线程函数。 while(1){sleep(1); printf("我是主线程\n");}
}

(3)字符串的传入与接收
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void *task(void *arg)
{char *num=(char *)arg;while(1){sleep(1); printf("我是子线程,传入参数为%s\n", num);}
}int main()
{char *string="i love you!";pthread_t thread; //定义线程句柄pthread_create(&thread, NULL, task, (void *)string); //创建线程,并绑定线程函数。 while(1){sleep(1); printf("我是主线程\n");}
}

(4)结构体的传入与接收
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>struct people{char *name;int age;
};void *task(void *arg)
{struct people *student=(struct people*)arg;while(1){sleep(1); printf("我是子线程,传入参数名字:%s,年龄:%d\n", student->name,student->age);}
}int main()
{struct people *student;pthread_t thread; //定义线程句柄//将结构体赋值student->age=10;strcpy(student->name, "John"); pthread_create(&thread, NULL, task, (void *)student); //创建线程,并绑定线程函数。 while(1){sleep(1); printf("我是主线程\n");}
}

2. 线程的退出
函数 pthread_exit 表示线程的退出。其传入的的参数可以被其它线程用 pthread_join 等待函数进行捕获。例如: pthread_exit( (void*)3 ); 表示线程退出,并将 3作为返回值被后面的 pthread_join 函数捕获。该函数的作用就是可以将该线程的计算结果传递给创建它的主线程或者其他线程。
注意1:在使用线程函数时,不能随意使用 exit 退出函数进行出错处理,由于 exit 的作用是使调用进程终止,往往一个进程包括了多个线程,所以在线程中通常使用 pthread_exit 函数来代替进程中的退出函数 exit。
注意2:如果线程退出的返回值是在线程内部定义的局部变量的话,记得加static修饰,增加变量的生命周期直至进程结束而结束。不然线程退出后,该片内存地址会立刻被销毁,此时返回的空间无效。或者将返回值修改为全局变量。
void pthread_exit(void *retval);
//void *retval:作为线程退出时的值。如果不需要捕获该值,可以传入NULL。
3. 线程的等待
当父线程结束的时候,如果没有 pthread_join 函数等待子线程执行的话,父线程会退出,而主线程的退出会导致进程的退出,故子线程也会退出。
由于一个进程中的多个线程是共享数据段的,因此通常在线程退出之后,退出线程所占用的资源并不会随着线程的终止而得到释放。正如进程之间可以通过 wait()函数系统调用来同步终止并释放资源一样,线程之间也有类似的机制,那就是 pthread_join 函数。这个函数是一个线程阻塞函数,调用这函数的线程将一直等待直到被等待的线程结束为止,当函数返回时,被等待线程的资源被回收。
int pthread_join(pthread_t pthid, void **thread_return);
//pthread_t pthid :线程句柄。
//void **thread_return : 用于接收线程退出的返回值。如果没有需要接收的填NULL。
上述函数第二个参数为二级指针,所以我们可以定义一级指针再取地址来获取该值,然后再进行强制类型转换为(void **)。
举例:


思考:上述例程中为什么要用static来修饰变量呢?
答:这是因为当线程退出时,其在内部定义的变量都会被销毁,可以将变量设为全局变量或者使用static来修饰变量,增加其生命周期,直至进程结束,才会被销毁。不然返回的地址将是一个已经被销毁的地址,无法使用。
补充
一般线程的退出和线程的等待是一起使用的。这里我们使用几种参数类型进行举例。
(1)返回整型
//退出:
static int a=10;
pthread_exit((void *) &a);
//-------------------------------//等待接收:
int *thread_result;
pthread_join(thread,(void **)&thread_result);
printf("子线程的返回值为:%d\n", *thread_result);
(2)返回浮点数
//退出:
static float f=10.12;
pthread_exit((void *) &f);
//-------------------------------
//等待接收:
float *thread_result;
pthread_join(thread,(void **)&thread_result);
printf("子线程的返回值为:%f\n", *thread_result);
(3)返回字符串
//退出:
static char *string="i love you!";
pthread_exit((void *)string);
//-------------------------------
//等待接收:
char *thread_result;
pthread_join(thread,(void **)&thread_result);
printf("子线程的返回值为:%s\n", thread_result);
(4)返回结构体
typedef struct
{char *name; //定义指针,后续使用时要为其开辟内存空间! 如果不想开辟空间,则使用数组。int age;
}people;//退出:people *student = (people *)malloc(sizeof(people)); //给结构体开辟空间,然后赋值。student->name=(char *)malloc(sizeof(char *)); //为结构体里定义的字符指针开辟内存空间。//给结构体赋值strcpy(student->name,"john");student->age=12;pthread_exit((void *)student);//等待接收student *thread_result;pthread_join(thread,(void **)&thread_result);printf("名字为:%s,年龄为:%d\n", thread_result->name,thread_result->age);
四、综合举例
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>void *task1(void * arg) //线程1函数
{float *num =(float *)arg;while(1){sleep(1);printf("这是子线程1,传入的值为%f\n",*num);}
}void *task2(void * arg) //线程2函数
{int *num =(int *)arg;static char *string="pthread2_end";while(1){ sleep(1);printf("这是子线程2,传入的值为%d\n",*num);(*num)--;if(*num <0) break;}pthread_exit((void *)string);
}int main()
{float pthread1_arg=11.2; //传入线程函数1的参数int pthread2_arg=3; //传入线程函数2的参数char *string;int ret;pthread_t thread1,thread2;//线程句柄//创建线程1ret=pthread_create(&thread1, NULL, task1, (void *) &pthread1_arg);if(ret<0) {perror("pthread1_create error!\n");return -1;}//创建线程2ret=pthread_create(&thread2, NULL, task2, (void *) &pthread2_arg);if(ret<0) {perror("pthread2_create error!\n");return -1;}pthread_join(thread2, (void **)&string);printf("%s\n",string);return 0;
}

相关文章:
Linux系统编程(五)多线程创建与退出
目录 一、基本知识点二、线程的编译三、 线程相关函数1. 线程的创建(1)整型的传入与接收(2)浮点数的传入与接收(3)字符串的传入与接收(4)结构体的传入与接收 2. 线程的退出3. 线程的…...
计算机毕业设计 | SpringBoot个人博客管理系统(附源码)
1,绪论 1.1 背景调研 在互联网飞速发展的今天,互联网已经成为人们快速获取、发布和传递信息的重要渠道,它在人们政治、经济、生活等各个方面发挥着重要的作用。互联网上发布信息主要是通过网站来实现的,获取信息也是要在互联网中…...
字母的大小写转换
自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在Python中,字符串对象提供了lower()方法和upper()方法进行字母的大小写转换,即可用于将大写字母转换为小写字母或者将小写字…...
JTW结构
JTW(JSON Web Token)的结构 在这篇笔记中,我们将了解JTW(JSON Web Token)的结构。我们将看到JTW是如何创建的,令牌的各个部分是什么,以及您如何自己构建和构造JTW。您还将了解一些这种结构的含义,以及使用JTW进行授权时的一些结果优缺点。 基本上,JTW本质上就是一个…...
debian11安装留档@VirtualBox
因为debian12无法安装tpot,所以又把11重新安装一遍,以前的安装文档:安装Debian 11 留档-CSDN博客 下载光盘 华为云地址:https://repo.huaweicloud.com/debian-cd/11.0.0/amd64/iso-cd/ 使用了debian11 教育版,比较有…...
SpringBoot——整合Thymeleaf模板
目录 模板引擎 新建一个SpringBoot项目 pom.xml application.properties Book BookController bookList.html 编辑 项目总结 模板引擎 模板引擎是为了用户界面与业务数据分离而产生的,可以生成特定格式的页面在Java中,主要的模板引擎有JSP&…...
电商推荐系统+电影推荐系统【虚拟机镜像分享】
电商推荐系统电影推荐系统【虚拟机镜像分享】 所有组件部署好的镜像下载(在下面),仅供参考学习。(百度网盘,阿里云盘…) 博主通过学习尚硅谷电商推荐电影推荐项目,将部署好的虚拟机打包成ovf文…...
(函数)判断素数(C语言)
一、运行结果; 二、源代码; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>//声明素数判断函数; void prime(int number);int main() {//初始化变量值;int number 0;//获取用户输入的数据;printf(&quo…...
git 学习随笔
git 学习随笔 基本概念 git 对待数据类似快照流的形式而不是类似 cvs 那样的纪录文件随时间逐步积累的差异 git 中所有数据在存储钱都会计算校验和(hash) 三种状态:已提交(committed),已修改(modified),已暂存(staged)。 add…...
【因果推断python】1_因果关系初步1
目录 为什么需要关心因果关系? 回答不同类型的问题 当关联确实是因果时 为什么需要关心因果关系? 首先,您可能想知道:它对我有什么好处?下面的文字就将围绕“它”展开: 回答不同类型的问题 机器学习目…...
(函数)颠倒字符串顺序(C语言)
一、运行结果; 二、源代码; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h> # include <string.h>//声明颠倒函数; void reverse(char a[]) {//初始化变量值;int i, j;char t;//循环颠倒;for (i 0, j strl…...
自定义数据集上的3D目标检测:使用OpenPCDet训练CenterPointPillar模型
前言 在自动驾驶和机器人领域,3D目标检测是关键技术之一。它能够提供关于周围环境中物体的精确位置和尺寸信息。OpenPCDet是一个基于PyTorch的开源3D目标检测框架,支持多种3D检测网络。在本文中,我们将探讨如何使用OpenPCDet框架和CenterPoi…...
音乐传奇告别之作:《杰作》未解之谜❗❗
坂本龙一的《杰作》不仅是一部音乐会纪录电影,更是他赠予世界的一封深情告别信。 这部影片精心收录了这位音乐巨匠生前最后一场钢琴独奏音乐会的珍贵瞬间, 其中涵盖了《圣诞快乐,劳伦斯先生》、《末代皇帝》、《水》等二十首令人陶醉的经典…...
【Postman接口测试】第四节.Postman接口测试项目实战(上)
文章目录 前言一、项目介绍 1.1 项目界面功能介绍 1.2 项目测试接口介绍 1.3 项目测试接口流程二、HTTP协议三、接口测试中接口规范四、项目合同新增业务介绍 4.1 登录接口调试 4.1 登录接口自动关联 4.1 添加课程接口调试 4.1 上传合同…...
opencv学习备份
2019年3月9日 指针地址向后走 int a[5] {1,100,200,300} int *pa –>*p1 –>*p100 指针移动 –> p0x00000000 内存地址 int a1 int *p&a 指针赋值常量,需要加寻址符号 –>*p1 –>*p CV_8UC1,CV_8UC2,CV_8UC3。 (最后的…...
Unity 中获取调用者方法名
介绍 在 Unity 开发中,有时需要在代码中获取当前方法的调用者方法名,以便进行日志记录、调试等操作。本教程将详细介绍如何使用 C# 中的 StackTrace 类来实现这一功能,并将其封装成一个便捷的工具类,以方便在项目中的任何地方…...
k8s集群中pod的容器资源限制和三种探针
一、资源限制 总结: requests表示创建pod时预留的资源,limits表示pod能够使用资源的最大值。requests值可以被超,limits值不能超过,如果是内存使用超过limits会触发oom然后杀掉进程,如果是cpu超过limits会压缩cpu的使用…...
tar 详细说明
tar命令在Unix和类Unix系统中被广泛用于打包和压缩文件。以下是对tar命令的详细说明: 一、命令概述 tar命令的名称来源于“tape archive”(磁带存档),最初设计用于在磁带上创建备份。现在,它已成为在Unix和类Unix系统…...
渗透测试工具Cobalt strike-2.CS基础使用
三、结合metasploit,反弹shell 在kali中开启使用命令开启metasploit msfconsole ┌──(root㉿oldboy)-[~] └─# msfconsole --- msf6 > use exploit/multi/handler [*] Using configured payload generic/shell_reverse_tcp --- msf6 exploit(multi/handler) > show …...
【UE5.1 角色练习】08-物体抬升、抛出技能 - part2
目录 前言 效果 步骤 一、让物体缓慢的飞向手掌 二、向着鼠标方向发射物体 前言 在上一篇(【UE5.1 角色练习】08-物体抬升、抛出技能 - part1)的基础上继续完成角色将物体吸向手掌,然后通过鼠标点击的方向来发射物体的功能。 效果 步骤…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...
LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...
【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...
