Linux中线程的实现,线程的接口相关函数pthread_create、pthread_join、pthread_exit

目录

一.线程的概念

 二.操作系统中线程的实现

三.Linux中线程的实现

四.进程与线程的区别

五.线程的接口相关函数

5.1 pthread_create

5.2 pthread_join

5.3 pthread_exit

六.代码演示

七.如何解决上述问题?

方案1.

方案2.

方案3.


一.线程的概念

进程是一个动态的概念,就是一个程序正在执行的过程。线程就是进程内部的一条执行路径,或者一个执行序列。

 二.操作系统中线程的实现

操作系统中,线程的实现有以下三种方式:

  • 内核级线程:开销大,但可以使用多处理器资源,实现真正意义上的并行。
  • 用户级线程:开销小,但无法使用多处理器资源。
  • 组合级线程

 

三.Linux中线程的实现

Linux实现线程的机制非常独特,从内核的角度来讲,它并没有线程这个概念;

Linux把所有的线程都当作进程来实现。

内核并没有准备特别的调度算法或是定义特别的数据结构来表征线程。

相反,线程仅仅被视为一个与其他进程共享某些资源的进程。每个线程都拥有唯一隶属于自己的 task struct,所以在内核中,它看起来就像是一个普通的进程(只是线程和其他一些进程共享某些资源,如地址空间)。 

四.进程与线程的区别

1.进程是资源分配的最小单位,线程是CPU调度的最小单位;

2.进程有自己的独立地址空间,线程共享进程中的地址空间;

3.进程的创建消耗资源大,线程创建相对消耗小;

4.进程的切换开销大,线程的切换开销相对较小

五.线程的接口相关函数

5.1 pthread_create

用于创建线程,成功返回0,失败返回错误码。

int pthread_create(pthread_t *restrict thread,
                          const pthread_attr_t *restrict attr,
                          void *(*start_routine)(void *),
                          void *restrict arg);

thread:接收创建的线程的ID;

attr:指定线程的属性,一般不设置线程属性为NULL;

start_routine:指定线程函数,这个线程函数的参数为void*,返回值也为void*;这是一个函数指针;

arg:给线程函数传递的参数(线程刚启动,线程函数的参数为void*,给它传参就是void*)

5.2 pthread_join

等待thread指定的线程退出,线程未退出时,该方法阻塞。(有点像父进程等待子进程结束的wait,或者说合并线程)

int pthread_join(pthread_t thread, void **retval);

retval:接收thread线程退出时,指定的退出信息。

5.3 pthread_exit

退出线程

int pthread_exit(void* retval);

retval:指定退出信息。

六.代码演示

void* thread_fun(void* arg)
{
	printf("hello fun!\n");
}

int main()
{
	pthread_t id;//线程的id
	pthread_create(&id,NULL,thread_fun,NULL);
	printf("hello main!\n");

	exit(0);
}

注:id开始是没有值的,执行pthread_create之后填充值;

运行结果:

此进程中包含两条线程,两条线程同时执行,有可能先 printf("hello main!\n");之后退出进程,printf("hello fun!\n");还未来得及执行;

有可能先printf("hello fun!\n");再printf("hello main!\n");

七.如何解决上述问题?

方案1.

使用pthread_join函数,这个是等待线程结束或者说合并线程。

我们让子线程循环10次,让主线程循环5次;

我们在主函数完成了自己想要做完的事情以后,调用pthread_join函数;

代码演示:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <pthread.h>

void* thread_fun(void* arg)
{
	for(int i=0;i<10;i++)
	{
		printf("hello fun!\n");
		sleep(1);
	}
	pthread_exit("thread_fun over!\n");
}

int main()
{
	pthread_t id;//线程的id
	pthread_create(&id,NULL,thread_fun,NULL);
	for(int i=0;i<5;i++)
	{
		printf("hello main!\n");
		sleep(1);
	}
	char* s=NULL;
	pthread_join(id,(void**)&s);//不传递退出信息时,第二个参数可以用NULL进行占位
	printf("join:s=%s\n",s);
	exit(0);
}

运行结果:

 利用pthread_join函数带出一个全局变量的值:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <pthread.h>

int data=200;
void* thread_fun(void* arg)
{
	for(int i=0;i<10;i++)
	{
		printf("hello fun!\n");
		sleep(1);
	}
	//pthread_exit("thread_fun over!\n");
	pthread_exit((void*)&data);
}

int main()
{
	pthread_t id;//线程的id
	pthread_create(&id,NULL,thread_fun,NULL);
	for(int i=0;i<5;i++)
	{
		printf("hello main!\n");
		sleep(1);
	}
	//char* s=NULL;
	//pthread_join(id,(void**)&s);//不传递退出信息时,第二个参数可以用NULL进行占位
	//printf("join:s=%s\n",s);
	int* p=NULL;
	pthread_join(id,(void**)&p);
	printf("join data=%d\n",*p);
	exit(0);
}

 可以返回NULL,字符串,全局变量的地址,不能返回临时变量的地址。

注:

1.其实就是s指向子线程退出的字符串,类似于主线程获取子线程的退出信息;&s也可,就是不强转也可以,但是会有警告;也就是通过一个指针,去记录子线程返回的信息

2.不接收子线程结束的信息,传NULL即可;

子线程:pthread_exit(NULL);

主线程:pthread_join(id,NULL);

3.pthread join执行的时候会阻塞

4.当我们去等待一个子线程的结束,亦会释放它相应的资源.join会接收子线程反馈给主函数的信息,同时会释放子线程的有关资源.

5.不一定只创建一个子线程,可以创建多个子线程;

6..当然,主线程也可以不调用pthread_join,那么只要在主线程中继续做自己的事情就可以了;即非必须调用pthread_join;

如下面两个方案:

方案2.

把线程函数和主函数改为打印10次,加上sleep便于观察。

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <pthread.h>

void* thread_fun(void* arg)
{
	for(int i=0;i<10;i++)
	{
		printf("hello fun!\n");
		sleep(1);
	}
}

int main()
{
	pthread_t id;//线程的id
	pthread_create(&id,NULL,thread_fun,NULL);
	for(int i=0;i<10;i++)
	{
		printf("hello main!\n");
		sleep(1);
	}
	exit(0);
}

运行结果: 

把线程函数改为打印5次,主函数改为打印10次,加上sleep便于观察。

 主函数打印10次,线程函数打印5次。

把线程函数改为打印10次,主函数改为打印5次,加上sleep便于观察。

主函数打印5次,线程函数打印次数不定(多于5次)。 

所以一般来讲,我们会让主程序就是main程序运行到最后再结束,哪怕主程序什么都不干,也要让它去等待子函数结束。

方案3.

不要退出进程,退出线程(这种方法不太好)

使用pthread_exit函数;主函数里面加一句:

pthread_exit(NULL);

那么子线程就不会结束。(加不加exit(0)都一样)。

为什么说这个方法不太好?因为这个函数是退出线程的,我们通常将它用在子线程中;

子线程结束,就算没有调用pthread_exit(NULL),也不影响主线程的运行,因为主线程结束以后,系统会默认调用exit(0);

注:线程是进程里面的一条执行路径,进程结束了,线程自然也就结束了;多进程的时候,父进程,子进程各自退出是没有影响对方的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/436481.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】矩阵的压缩存储

矩阵的压缩存储 5.1 普通矩阵的存储 用二维数组存储 分为行优先和列优先&#xff1a; 行优先&#xff1a;优先存放一行的数据。 列优先&#xff1a;优先存放一列的数据。 注意下标是从0还是1开始的&#xff01; 5.2 对称矩阵的存储 对称矩阵定义 若n阶方阵中任意一个元素 a i …

Allure小白下载安装

1、下载官网地址&#xff1a;https://github.com/allure-framework/allure2/releases 2、下载安装包后需要解压到一个非中文名称路径下 3、配置环境变量 D:\Allure\allure-2.27.0\bin 我的电脑右键选择属性&#xff0c;高级系统设置&#xff0c;环境变量 4、CMD查看安装all…

Java | Java的输入与输出

文章目录 Java输出1、System.out.println()2、System.out.printf()3、System.out.print() Java输入1、使用Scanner类的对象获取输入&#xff08;1&#xff09;一般类型输入&#xff08;2&#xff09;字符串类型输入&#xff08;3&#xff09;char类型输入 2、使用System.in.rea…

挑战杯 基于深度学习的目标检测算法

文章目录 1 简介2 目标检测概念3 目标分类、定位、检测示例4 传统目标检测5 两类目标检测算法5.1 相关研究5.1.1 选择性搜索5.1.2 OverFeat 5.2 基于区域提名的方法5.2.1 R-CNN5.2.2 SPP-net5.2.3 Fast R-CNN 5.3 端到端的方法YOLOSSD 6 人体检测结果7 最后 1 简介 &#x1f5…

Android APP性能指标(二)

文章目录 一、响应时间1.1 数据获取1.2 响应时间指标测试点1.3 启动速度测试点1.4 响应时间测试解决方法 二、流量2.1 数据获取2.2 流量测试关注点2.3 测试标准 三、电量3.1 连接手机3.2 数据获取3.3 获取APP的UID3.3 重置电池数据收集数据3.4 电量指标测试 四、温度五、性能测…

Pyaudio的安装以及报错解决

Pyaudio是一个可以用麦克风录入声音的库&#xff0c;但我在安装时发现无论是在cmd中pip安装还是在Pycharm中安装&#xff0c;都会报一堆错误。因此写一篇我最终的解决方案&#xff0c;我的解决办法是采用离线安装的方式&#xff0c;安装pyaudio库。 一.下载离线安装包 离线安…

超详细——动态内存分配+柔性数组

☃️个人主页&#xff1a;fighting小泽 &#x1f338;作者简介&#xff1a;目前正在学习C语言和数据结构 &#x1f33c;博客专栏&#xff1a;C语言学习 &#x1f3f5;️欢迎关注&#xff1a;评论&#x1f44a;&#x1f3fb;点赞&#x1f44d;&#x1f3fb;留言&#x1f4aa;&am…

意外之失:不小心删除的文件如何寻回?

一、瞬间消失的珍贵记忆 在我们的日常电脑使用中&#xff0c;总有一些时刻让人心惊胆战——那就是不小心删除了重要的文件。或许是一个珍藏多年的照片集&#xff0c;或许是一个即将完成的项目文档&#xff0c;这些文件承载着我们的回忆、努力和成果&#xff0c;却在一次疏忽之…

文本溢出隐藏 显示省略号,鼠标悬浮展示 el-tooltip(TooltipIsShowMixin封装)

目录 mixins 封装使用 TooltipIsShowMixin效果展示 mixins 封装 TooltipIsShowMixin.js export const TooltipIsShowMixin {data() {return {tooltipIsShow: false}},methods: {tooltipIsDisHandler(className) {this.$nextTick(() > {const dom document.querySelector…

m4a是什么文件格式?音质是无损吗?

m4a文件格式作为一种音频文件格式&#xff0c;源于MPEG-4标准的制定&#xff0c;该标准致力于提供高效的多媒体编解码方案。通常使用AAC编码&#xff0c;可以提供高质量的音频&#xff0c;但不是无损格式。AAC是一种有损压缩格式&#xff0c;它能够在减小文件大小的同时保持相对…

Jenkins 部署 RuoYi

目录 1、项目介绍 2、部署若依 clone 源代码 导入数据库到 node-16 修改 RuoYi 配置文件 推送 RuoYi 项目至 Gitlab 3、配置 Jenkins 配置本地 Maven Jenkins 配置 Maven 新建 Maven 项目 编写构建后的脚本 Jenkins 构建后执行脚本 4、Jenkins 传递参数 设置项目部…

Linux:kubernetes(k8s)探针的简介(7)

相当于我们用于监控容器的一个东西&#xff0c;通过给我们的不同探针来判断容器内的状态情况&#xff0c;实现故障的一个自动恢复。 我们在上一章的yaml文件中写入了一个这个东西 那么他什么情况下重启&#xff0c;就是通过探针进行一个监控&#xff0c;加入监控某一个端口&…

element多选框select下拉框数据回显的问题value.push is not a function

文章目录 问题描述 问题描述 今天在使用Element UI el-select组件遇到了一个问题&#xff0c;如下图&#xff1a; 下拉框里的值选中了&#xff0c;但是文本框里没有值 这是 el-select组件代码,我这里是用了一个多选框&#xff0c;options的值是在后端查询的&#xff0c;form.we…

利用MATLAB模拟点电荷的电场分布

一、实验目的&#xff1a; 1&#xff0e;熟悉点电荷的电场分布情况&#xff1b; 2&#xff0e;学会使用Matlab绘图&#xff1b; 二、实验内容&#xff1a; 1&#xff0e;根据库伦定律&#xff0c;利用Matlab强大的绘图功能画出单个点电荷的电场分布情况&#xff0c;包括电力…

虚拟化之内存(Memory)

一 内存的查看方式 free -k/m/h cat /proc/meminfodmesg |grep memory free命令的实质是根据meminfo中的文件来提取信息 二 内存虚拟化 1.概念&#xff1a;由于物理MMU只能通过Host机的物理地址进行寻址&#xff0c;所以实现内存虚拟化&#xff0c;关键是需要将Guest机的…

⭐每天一道leetcode:58.最后一个单词的长度(简单;istringstream;遍历查看)

⭐今日份题目 给你一个字符串 s&#xff0c;由若干单词组成&#xff0c;单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。 示例1 输入&#xff1a;s "Hello World" 输出&#xff1a…

Ubuntu将c++编译成.so文件并测试

一、准备cpp和h文件 创建test.cpp 在cpp中定义相加的函数funcAdd&#xff0c;给出函数的细节代码 #include <iostream> using namespace std;int funcAdd(int x, int y) {return xy; }创建test.h 在h中声明定义的函数&#xff0c;不需要任何细节 #ifndef __TEST__ #…

Catf1agCTF-Web通关合集

本文相关的ctf平台链接&#xff1a;Catf1agCTF - 综合训练平台 WEB签到 查看源代码 flag倒叙了&#xff0c;可以使用下面这个 文本字符串倒序在线工具(ESJSON在线工具) flag&#xff1a; catf1ag{welcome_to_catf1agctf_!!!_gogogo_!!!} flag在哪呢&#xff1f; 查看源代…

1、MQ_介绍、优缺点、类型等

MQ介绍 1. MQ概述 MQ&#xff08;Message Queue&#xff09;&#xff1a;消息队列&#xff0c;是基础数据结构中FIFO&#xff08;first in first out&#xff09;的一种数据结构。一般用来解决流量削峰、应用解耦、异步处理等问题&#xff0c;实现高性能&#xff0c;高可用&a…