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;//线程的idpthread_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;//线程的idpthread_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;//线程的idpthread_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;//线程的idpthread_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);

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用 Docker 部署 Stirling-PDF 多功能 PDF 工具

1&#xff09;Stirling-PDF 介绍 大家应该都有过这样的经历&#xff0c;面对一堆 PDF 文档&#xff0c;或者需要合并几个 PDF&#xff0c;或者需要将一份 PDF 文件拆分&#xff0c;又或者需要调整 PDF 中的页面顺序&#xff0c;找到的线上工具 要么广告满天飞&#xff0c;要么 …

小程序API能力集成指南——画布API汇总(四)

CanvasContext canvas 组件的绘图上下文。 方法如下&#xff08;3&#xff09;&#xff1a; scale CanvasContext.scale CanvasContext.scale(number scaleWidth, number scaleHeight) 功能描述 在调用后&#xff0c;之后创建的路径其横纵坐标会被缩放。多次调用倍数会相…

Flutter性能优化

性能分析工具 &#xff08;1&#xff09;performance overlay 开启performance overlay后&#xff0c;Flutter APP上将显示一个展示一个浮层&#xff0c;浮层中会实时展示当前的UI线程及Raster线程的运行情况。如果都是蓝色竖条&#xff0c;说明界面运行流畅&#xff0c;否则则…

【CSP考点回顾】前缀和数组

一、一维数组前缀和 前缀和算法是一种用于处理数组的技术&#xff0c;它可以快速计算任何连续子数组的和。适合在多次查询中需要求解多个范围和的情况。使用前缀和算法可以将每次求和的时间复杂度从 O(n) 降低到 O(1)。 前缀和的思想是创建一个新数组 A r r Arr Arr&#xff0…

递增三元组(第九届蓝桥杯)

文章目录 题目原题链接思路分析二分做法1二分做法2双指针做法前缀和解法 题目 原题链接 递增三元组 思路分析 由时间复杂度可知需要至少优化到 O ( n l o g n ) O(nlogn) O(nlogn)才行 而纯暴力枚举三个数组的话&#xff1a; O ( n 3 ) O(n^3) O(n3) 可以考虑将b[]作为标志&…

RabbitMQ 交换器

RabbitMQ 交换器 官方例子 http://www.rabbitmq.com/getstarted.html direct 如上图所示&#xff0c;两个队列绑定到了direct交换器上&#xff0c;第一个队列绑定的 binding key 为 orange &#xff0c;第二个队列有两个绑定&#xff0c;分别是 black 和 green 。 如上图所示…

基础小白快速入门web前端开发技术------>web概述

Web概述 我们在编程的学习中&#xff0c;随着学习的深入&#xff0c;我们会理解到WEB这个东西&#xff0c;那么 web究竟是个啥&#xff0c;到底该咋用&#xff1f; web&#xff0c;是网站的英文意思&#xff0c;又被称作“下一代Web3.0&#xff0c;互联网”&#xff0c;是在We…

vue在线查看pdf文件

1.引入组件 npm install --save vue-pdf2、pdf组件页面模板 <template><div class"scrollBox" ><el-dialog :visible.sync"open" :top"1" width"50%" append-to-body><div slot"title"><el…

混沌工程-经典案例分享

目录 前言 案例 1、强弱依赖不合理 2、预案不生效 3、异常数据不兼容 4、监控体系缺陷 5、系统缺整体架构设计 总结 前言 我们公司从启动混沌工程到现在已经几乎覆盖了线上的所有核心业务&#xff0c;先后进行过2000次演练共挖掘出120个漏洞。这些漏洞有些得了及时修复…

DevEco Studio编译时候自动生成js、js.map文件导致项目无法运行

这算是开发工具的bug吧&#xff0c;估计后期能修复。 解决办法&#xff0c;手动删除.js、.js.map文件即可&#xff1b; 文件太多&#xff0c;删除很麻烦&#xff0c;有个大佬写了个插件&#xff0c;可一键删除&#xff1a;ArkCompilerSupport DevEco Studio 运行项目有时会自动…

二叉树进阶leetcode

606. 根据二叉树创建字符串 要点&#xff1a;前序遍历&#xff0c;当左子树为空时&#xff0c;右结点有数字时要给左边加括号 class Solution { public:string tree2str(TreeNode* root) {string s;//创建一个字符串if(rootnullptr){return s;}sto_string(root->val);//保存…

网络、网络协议模型、UDP编程——计算机网络——day01

今天来到了网络编程&#xff0c;主要讲了网络、网络协议模型以及UDP编程 网络 网络主要是进行&#xff1a;数据传输和数据共享 网络协议模型 OSI协议模型应用层 实际发送的数据表示层 发送的数据是否加密会话层 是否建立会话连接传…

调研图基础模型(Graph Foundation Models)

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 图基础模型&#xff08;Graph Foundation Models&#xff0c;简称 GFMs&#xff09; 是一种经过预训练的图大模型&#xff0c;旨在处理不同领域的图数据和任务。让我们详细探讨一下这个概念。 Github …

uniapp让输入框保持聚焦状态,不会失去焦点

使用场景&#xff1a;当输入框还有发送按钮的时候&#xff0c;点击发送希望软键盘不消失&#xff0c;还可以继续输入&#xff0c;或者避免因输入图片标签造成的屏闪问题 多次尝试后发现一个很实用的方法&#xff0c;适用input输入框和editor输入框 解决办法&#xff1a;把cli…

AXI4总线解析

一、读地址 AWVALID和AWREADY同时为高时&#xff0c;在这个上升沿&#xff0c;图中黄线&#xff0c;将接下来的数据写入地址40000000中。 在

基于状态机的按键消抖实现

摸鱼记录 Day_14 !(^O^)y review 在day_13中以按键状态判断为例学习了状态分析基于状态机的按键消抖原理-CSDN博客 分析得到了下图&#xff1a; 今日任务&#xff1a;完成此过程 !(^O^)y 小梅哥对应视频&#xff1a; 15B 基于状态机的按键消抖Verilog实现_哔哩哔哩…

DataFunSummit 2023:洞察现代数据栈技术的创新与发展(附大会核心PPT下载)

随着数字化浪潮的推进&#xff0c;数据已成为企业竞争的核心要素。为了应对日益增长的数据挑战&#xff0c;现代数据栈技术日益受到业界的关注。DataFunSummit 2023年现代数据栈技术峰会正是在这样的背景下应运而生&#xff0c;汇聚了全球数据领域的精英&#xff0c;共同探讨现…

Linux 文件操作命令

1 文件与目录操作 cd /home 进入 ‘/home’ 目录 cd .. 返回上一级目录cd ../.. 返回上两级目录cd - 返回上次所在目录cp file1 file2 将file1复制为file2cp -a dir1 dir2 复制一个目录 cp -a /tmp/dir1 . 复制一个…

【Qt】初识Qt

文章目录 一. 行业岗位介绍二. 什么是客户端&#xff1f;三. GUI 开发的各自技术方案四. 什么是框架&#xff1f;五. Qt 的发展史五. Qt 支持的系统六. Qt 的优点 一. 行业岗位介绍 二. 什么是客户端&#xff1f; 既然 Qt 是用来进行客户端开发的&#xff0c;那我们就要了解什…

如何保证ES和数据库的数据一致性?

在业务中&#xff0c;我们通常需要把数据库中的数据变更同步到ES中&#xff0c;那么如何保证数据库和ES的一致性呢&#xff1f;通常有以下几种做法&#xff1a; 双写 在代码中&#xff0c;对数据库和ES进行双写&#xff0c;并且先操作本地数据库&#xff0c;后操作ES&#xff…