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.hqwc.cn/news/518152.html

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

相关文章

spring-data-elasticsearch官方文档解读(部分)

Spring Data Elasticsearch 这里主要学习的是4.4.16版本的文档 1. 版本 下表显示了 Spring Data 发行版系列使用的 Elasticsearch 版本和其中包含的 Spring Data Elasticsearch 版本&#xff0c;以及引用该特定 Spring Data 发行版系列的 Spring Boot 版本。给出的 Elastics…

数据结构——lesson6二叉树基础

前言 hellohello~这里是土土数据结构学习笔记&#x1f973;&#x1f973; &#x1f4a5;个人主页&#xff1a;大耳朵土土垚的博客 &#x1f4a5; 所属专栏&#xff1a;数据结构学习笔记 &#x1f4a5;对于数据结构顺序表链表有疑问的都可以在上面数据结构的专栏进行学习哦~感…

优思学院|使用完全数据计算CPK需要分子组吗?

使用全部数据进行计算&#xff0c;那么这种计算更类似于评估过程的PPK&#xff0c;PPK与计算CPK是不一样的&#xff0c;因为当数据以子组形式收集时&#xff0c;可以很容易地根据每个子组的范围或子组标准差来计算各个子组内的变异性。这是因为每个子组包含多个数据点&#xff…

你所需要的是 Wide Events,而不是 “Metrics、Logs 和 Traces”

原文[0] &#xff1a;Ivan Burmistrov - 2024.02.15 这段引自 Charity Majors 的话&#xff0c;或许是对当前科技行业可观测性状况的最佳概括——一个全面的、大规模的混乱。每个人都感到困惑&#xff0c;什么是 trace&#xff1f;什么是 span&#xff1f;一条 log 是 span 吗…

【C++庖丁解牛】模版初阶

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 目录 1. 泛型编程2. 函数模…

Python Module level import not at top of file (E402)

Python Module level import not at top of file 引言正文 引言 这里给大家简单介绍一下当我们使用 Pycharm 编译器时遇到的 Python Module level import not at top of file 提醒。 正文 请看下图&#xff1a; 这时就会提示我们这个信息&#xff0c;并且 import 下面会出…

【北京迅为】《iTOP-3588开发板网络环境配置手册》第3章 开发板直连电脑配置方法(不能上外网)

RK3588是一款低功耗、高性能的处理器&#xff0c;适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用&#xff0c;RK3588支持8K视频编解码&#xff0c;内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…

STM32CubeMX学习笔记13 ---IIC总线

1、IIC 简介 IIC(Inter&#xff0d;Integrated Circuit)总线是一种由NXP&#xff08;原PHILIPS&#xff09;公司开发的两线式串行总线&#xff0c;用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信&#xff0c;在小数据量场合使用&#xff0c;传输距离短&…

算法优化 —— 解码阶段的特征融合篇

文章目录 一、UNet v2&#xff08;2023&#xff09; 一、UNet v2&#xff08;2023&#xff09; 代码链接 提出的SDI模块&#xff0c;通过将高级特征的语义信息与低级特征的细粒度信息通过哈达马乘积相结合&#xff0c;增强每级特征图。

Docker-部署若依项目

文章目录 后端一、搭建局域网二、redis安装测试 三、MySQL安装四、后端项目放入位置及使用Dockerfile自定义镜像后端项目放入位置 前端配置检查各个端口是否启动nginx部署 首先得先把内部的文件给删除清空 docker images–查看有哪些文件 docker rmi -f ID–删除ID 后端 一、…

国创证券|沪指震荡微跌,资源股集体拉升,黄金概念持续活跃

7日早盘&#xff0c;两市股指盘中震动下探&#xff0c;创业板指、科创50指数跌超1%&#xff0c;北证50指数跌逾2%&#xff1b;北向资金小幅流出。 截至午间收盘&#xff0c;沪指跌0.16%报3035.04点&#xff0c;深证成指跌0.68%&#xff0c;创业板指跌1.48%&#xff0c;科创50指…

基于OpenCV的图形分析辨认02

目录 一、前言 二、实验目的 三、实验内容 四、实验过程 一、前言 编程语言&#xff1a;Python&#xff0c;编程软件&#xff1a;vscode或pycharm&#xff0c;必备的第三方库&#xff1a;OpenCV&#xff0c;numpy&#xff0c;matplotlib&#xff0c;os等等。 关于OpenCV&…