Linux——线程(2)

news2025/1/20 0:52:01
在上一篇博客中我介绍了Linux中的线程是什么样的,就如同进程可以通过
fork创建,可以被终止,可以退出一样,线程也可以被我们用户控制,这
篇博客我会介绍线程的控制,并且基于线程的控制所产生的一些问题进行
解决这些问题

线程的控制

1. 线程的创建

我们要学会对线程的控制,首先得创建出来一个线程,所以我们会认识一个接口:
在这里插入图片描述
这就是我们创建线程的一个接口,可以看出它是三号手册中的,并不是系统调用,这一点之后会从多方面来解释它。
它的第一个参数就是线程的id,类型是ptread_t,这是一个输出型参数需要用户自己传入,然后它给你带出线程的id。
第二个参数是有关于线程的属性的参数,我们现在不谈论,传参时传空指针就可以。
第三个参数是一个函数指针,这个函数的参数是void*,返回值也是void*,这个函数也就是线程要执行的函数。
这第四个参数就是第三个参数中函数的参数,它是以这种方式来传的。
下面我们就可以来编写一简单的代码了(其实也是我们上一章博客中所写的):
在这里插入图片描述
在这里插入图片描述
当我们写好代码之后直接使用g++编译的时候会出现下面的情况:
在这里插入图片描述
这其实是因为pthead_create函数,它不是系统调用,它是存在于库中的,但是它也不是C/C++标准库,所以我们在编译的时候要加上这么一个选项:
在这里插入图片描述
而pthread库它有一种叫法,叫做系统原生库,顾名思义就是只要有操作系统,那么就一定会携带这个库。
那么这个库为什么会形成单独的库,而不是和系统代码在一起呢?我们上一章说过Linux中不存在真正意义上的线程,只存在轻量级进程,而对于用户来说,我们刚接触Linux的话哪能知道什么是轻量级进程,我们只知道线程,所以操作系统就必须实现出关于线程的一套接口便于用户使用,这也就会形成了一个pthread库了,不和系统代码在一起是因为Linux中真正意义上根本没有线程这个说法。

a. 多线程的创建

既然能创建一个线程,那我们就能够创建多个线程:
在这里插入图片描述
在这里插入图片描述
在这段代码所演示的结果中我们可以看到几个现象:
task_thread函数被多个执行流同时执行,那就说明这个函数被重入了。
CPU对于线程的调度也是随机的,这一点是理所应当的,因为我们刚开始解除CPU的调度的时候,它的调度就不是完全按顺序来的。
假如现在我们对四号线程进行除0的操作:
在这里插入图片描述
在这里插入图片描述
可以看到一个线程出现异常之后,所有的线程都会被终止,这也印证了我们上一篇博客中所说的。
但是我们发现我们上面的代码有些挫,并且我们要知道线程创建的接口中的那个函数的参数是void*的,这就意味着我们可以传任意类型的参数。那么我们的代码可以是这样的:
在这里插入图片描述

在这里插入图片描述
这里打印出现不规整的现象也是因为CPU调度的时候时间片到了之后切换线程,然后导致输出时,语言缓冲区中的数据出现了错乱。这也说明该函数中含有输出打印函数,这个输出函数也是不可重入的。
我们此时再让这个主线程打印一下线程的id,同时我们再查看一下线程的lwp:
在这里插入图片描述

在这里插入图片描述
我们发现线程的id和lwp竟然不是同一个值,而这其中的原委我会在之后讲述,其实线程id它就是一个地址而已。
在这里我们介绍一个接口,它可以获取调用线程的线程id:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
我们可以看到主进程中打印的tid和线程函数中所打印的线程id是相等的,并且我们也可以打印主线程的id。

2. 线程的控制

接下来我们要来认识一些关于线程控制的接口。

a. 线程的终止

我们首先要学会线程的终止
凭借return可以直接终止进程:
在这里插入图片描述

在这里插入图片描述
但是要注意exit函数是用来终止一个进程的,如果把它作用在线程中,它会终止整个线程:
在这里插入图片描述

在这里插入图片描述
除了利用return之外,我们还可以使用pthread库中的函数pthread_exit:

在这里插入图片描述
它的参数跟return 一样能返回函数的返回值:
在这里插入图片描述

在这里插入图片描述

b. 进程的等待

在对进程的认识中进程退出后如果不回收的话,就会进入僵尸状态,而线程也是如此,但是它不是跟进程一样的僵尸状态,而是是类似于僵尸问题。所以就有了线程的回收函数:
在这里插入图片描述
这个函数就能够回收指定线程id的线程,回收成功返回0,回收失败返回一个错误数字:
在这里插入图片描述

在这里插入图片描述
我们在前面只是提及了一下线程执行函数的返回值,但是并没有说怎么获取,我们可以看到,我们是没有办法在主线程中直接得到这个线程函数的返回值的,所以就有了pthread_join的第二个参数。

线程函数的返回值

我们看到线程函数的返回值类型是void* 类型的,而pthread_join的第二个参数是void**的,这其实就是输出型参数,可以回想一下,当我们函数中有输出型参数时,传的其实是这个参数的指针,所以这里才是void*。当使用pthread_join成功回收线程之后,它的第二个参数就会指向线程函数的返回值:
在这里插入图片描述
在这里插入图片描述
就如同我们线程函数可以传任意类型一样,意味着我们的返回值也可以返回任意类型:
在这里插入图片描述
在这里插入图片描述
我们看到确实能够返回任意类型,但是这里的线程id的打印好像有问题,我上面说线程id就是一个地址,这个机器是64位的,所以使用int来存储地址会溢出所以这里我们更改一下:
在这里插入图片描述
在这里插入图片描述
这里可能会有人有问题,假如线程出了异常怎么办?这个等待函数中好像没有waitpid中的status啊,其实无需担心因为线程异常,整个进程都会退出,这就跟这个线程没有关系了,而是进程的问题了。所以编写线程代码时尽量不要让它出现异常。

c. 线程的分离

我们在使用线程的时候有时候并不需要它返回信息,只需要帮我们完成任务之后退出就可以了,所以为了取消pthread_join的消耗,我们可以将这个线程分离:
在这里插入图片描述
这个函数会将指定线程id的线程与主线程分离,然后就不需要关心它的返回和回收它了,这个线程会自动被操作系统回收,且如果主进程是不可以join该线程的。说是分离但其实这些线程还是共享资源,出了异常还是会终止所有进程:
在这里插入图片描述
在这里插入图片描述
当然这个线程分离函数可以让其它线程分离目标线程,也可以自己分离自己。

d. 线程的取消

这是线程终止的第三种方式:
在这里插入图片描述
我们可以来看看一个线程被取消之后,join它会发生什么:
在这里插入图片描述

在这里插入图片描述
我们看到线程函数种返回的是10,但是结果是-1。
这是因为当线程被取消之后,操作系统会将线程函数的返回值设为-1,而这个-1其实是一个宏:
在这里插入图片描述

我们再看线程分离之后能否被取消,以及取消之后能否被join呢?
在这里插入图片描述

在这里插入图片描述
我们能看到线程分离之后仍就能被取消,但是仍然无法join。

较深层次了解pthread库

我们在了解pthread库中的各种接口,但是这些接口都是三号手册中的接口,而它也不存在于C/C++标准库中,我说它其实是原生系统库。
我们这次从用户的视角跳的更高一些,以俯视的视角来认识一下Linux中的线程。我们知道Linux种并没有真正意义上的线程,它是使用进程模拟实现的线程,从而有了轻量级进程,但是对于用户而言,用户在刚使用Linux的时候可能并不认识什么是轻量级进程,他们只认识线程。所以Linux就必须提供关于能够呈现出线程行为的一套体系。但是因为Linux中没有线程,这个就需要在用户层和系统层之间添加一层,使用这一层来让轻量级进程呈现出线程的行为:
在这里插入图片描述
所以,一方面pthread需要向下封装Linux中的轻量级进程,一方面要对用户提供各种线程的接口。
而我们也知道,系统中会存在许多的线程,要完成以上两点,pthread库也必须能够管理这批被模拟成线程的进程,先组织再描述,pthread库中一定会有像struct tcb这样的结构体,这个结构体肯定封装着一个lwp,因为轻量级进程与被模拟出来的线程是一对一的。
而且线程拥有着自己的属性,比如上下文,比如栈空间,这些一定是要被以上的结构体纳入其中的。
在Linux中有这样一个系统调用:
在这里插入图片描述
这个是用来创建轻量级进程的系统调用,它也是创建进程整体的系统调用(fork的底层就是它),具体要创建哪一个这一点可以通过它的第三个参数flag实现。
它的第一个参数就是我们线程控制中创建线程所要执行的函数。第二个函数就是线程所使用的栈空间,可以看到它是一个指针。
那么pthread库又是怎么管理线程的呢?
当我们的程序中使用了pthread库之后,我们的程序在执行之后,pthread库也会被加载到内存中,同时与进程的共享区通过页表建立映射,但是我们说了,系统中会有很多的线程,也会有很多的进程,所以一定会出现多个进程地址空间指向跟pthread库建立映射的,这样pthread库就需要管理系统中的所有的线程:
在这里插入图片描述
而在实际场景中,pthread库是这样组织线程属性的:
在这里插入图片描述
在当线程库与地址空间建立映射之后,线程库中就会创建并初始化出关于线程的各种属性集,然后当再次创建新线程的时候,就会在地址上紧挨着上一个新线程属性集在创建初始化,这样看起来对线程的管理就像对一个数组增删查改一样。
在struct pthread结构体中一定存在这样的字段void* retval来存储线程函数的返回值,从而让pthread_join能够获取返回值。
地址空间中栈只有一个,那么线程的栈空间又是如何分配呢?要知道前面介绍的clone接口中关于栈空间的参数是一个指针,所以线程栈空间可以是在堆上创建,第一个线程使用的就是地址空间的栈区,而新线程的栈空间一般是建立在堆上。
我们看到无论是pthead库的函数接口还是pthread对于线程的组织以及管理的数据,它都是需要在进程的地址空间中的用户区建立的的,所以Linux中的线程也叫用户级线程

理解高级语言中的线程库

我们都知道现在的大部分的编程语言,诸如C++、java、python都支持了对应的线程库,我们现在就来简单的使用一下C++的线程库:
在这里插入图片描述
在这里插入图片描述
当我们编译好它之后再运行它,发现它运行不了,而当我们的编译选项中加上-pthread后,程序能够正常运行:
在这里插入图片描述
由此得知,它也只不过是封装了pthread库从而实现了自己的线程方法而已。

线程局部存储

我们使用一段代码来解释它是什么:
在这里插入图片描述

在这里插入图片描述

这段代码展示了,线程之间是可以共享全局变量的,但是我们在g_flag前加一个这个选项,:
在这里插入图片描述

在这里插入图片描述
这个全局变量好像变成每个线程私有的了,__thread是一个编译选项,它会将修饰的全局变量从所有线程共享的状态,变成每个线程独有的一个变量,而这个变量的存储也从全局数据区到了线程局部存储。
最后我在提供两个知识点,可以自行下去验证,在多线程中,线程是可以创建子进程的,这个子进程仍是该线程所属线程的子进程,线程也是可以使用exec*类的接口进行程序替换的,但是一般不建议,因为一个线程切换了代码块之后,可能会影响其他线程的代码执行。

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

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

相关文章

安装系统后,如何单个盘空间扩展多个盘空间?

1、计算机-管理-存储-磁盘空间 2、压缩C盘符,分出多余空间 3、将多余空间扩展,然后修改盘符名称

为什么会不断出现低价窜货链接

品牌在做控价的过程中,会进入一个怪圈,就是不管如何治理,低价、乱价、窜货链接都在不断出现,甚至有些低价链接会占据电商首页的位置,其实这些在一定程度上讲是正常的,品牌在不断发展,链接也是动…

Cyber RT 开发工具

在Cyber RT中还提供了一些工具,这些工具可以拓展Cyber RT功能、提高开发调试效率,本章主要介绍这些工具的使用。 本章内容: 1.cyber record工具的应用; 2.常用命令工具的使用; 学习收获: 1.可以通过cyber record将发布的话题消息…

基础50刷题之一(交替合并字符串)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、题目二、力扣官方题解(双指针)三、文心一言解释总结 前言 刚上研一,有人劝我好好学C,当时用的不多就没学&a…

Javascript进阶课程——大纲

JavaScript进阶教程_哔哩哔哩_bilibili

Windows下 OracleXE_21 数据库的下载与安装

Oracle 数据库的下载与安装 数据库安装包下载数据库安装访问数据库进行测试Navicat连接数据库 1. 数据库安装包的下载 1.1 下载地址 Oracle Database Express Edition | Oracle 中国 1.2 点击“下载 Oracle Database XE”按钮,进去到下载页面(选择对…

图论练习5

Going Home Here 解题思路 模板 二分图最优匹配,前提是有完美匹配(即存在一一配对)左右集合分别有顶标,当时,为有效边,即选中初始对于左集合每个点,选择其连边中最优的,然后对于每…

uniapp 解决请求出现 /sockjs-node/info?t=问题

1. uniapp请求出现 /sockjs-node/info?t问题 1.1. 问题 uniapp项目老是出现 http://192.168.2.106:8080/sockjs-node/info?t1709704280949 1.1. sockjs-node介绍 sockjs-node 是一个JavaScript库,提供跨浏览器JavaScript的API,创建了一个低延迟、全…

spring 注解缓存查询方法使用

spring-boot项目启动类上加注解: EnableCaching 查询方法注解: Cacheable

基于springboot的精准扶贫管理系统论文

精准扶贫管理系统 摘要 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了精准扶贫管理系统的开发全过程。通过分析精准扶贫管理系统管理的不足,创建了一个计算机管理精准扶贫管理系统的方案。文章介绍了精…

【Java JVM】Class 文件的加载

Java 虚拟机把描述类的数据从 Class 文件加载到内存, 并对数据进行校验, 转换解析和初始化, 最终形成可以被虚拟机直接使用的 Java 类型, 这个过程被称作虚拟机的类加载机制。 与那些在编译时需要进行连接的语言不同, 在 Java 语言里面, 类的加载, 连接和初始化过程都是在程序…

SpringBoot整合【RocketMQ】

目录 1.POM文件添加依赖及yml配置 2.RocketmqUtil 3.生产者&#xff08;异步发送示例&#xff09; 4.消费者 5.测试 1.POM文件添加依赖及yml配置 <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter&l…

html--3D爱心

文章目录 代码效果 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>爱心</title><style type"text/css">*{margin: 0px;border: 0px;}body{overflow: hidden;background-…

简单整理vue-router,路由知识

1.项目中引入 1.1 安装注册 1.2 封装抽离 在main.js中 书写,会造成单个js文件过于臃肿的情况,需要将路由配置部分抽离出来,在src下新建router文件夹,新建index.js文件 import Vue from vue import VueRouter from vue-router import HomeView from ../views/HomeView.vue im…

Wireshark——获取指定协议的数据包

1、问题 使用Wireshark捕获了大量的数据包&#xff0c;但是只想要指定协议的数据包。 2、方法 例如&#xff0c;只想要Modbus/TCP协议的数据包。 在应用显示过滤器中输入协议的名称&#xff08;小写&#xff09;&#xff0c;回车。 选择文件&#xff0c;导出特定分组。 将所…

Matlab 机器人工具箱 RobotArm类

文章目录 1 RobotArm1.1 方法1.2 注意2 RobotArm.RobotArm3 RobotArm.cmove4 其他官网:Robotics Toolbox - Peter Corke 1 RobotArm 串联机械臂类 1.1 方法 方法描述plot显示机器人的图形表示teach驱动物理和图形机器人mirror使用机器人作为从机来驱动图形</

影响哈默纳科Harmonic减速机使用寿命的5大因素

哈默纳科HarmonicDrive减速机以其轻量、小型、传动效率高、减速范围广、精度高等特点&#xff0c;被广泛应用于各种传动系统中。然而&#xff0c;尽管哈默纳科Harmonic减速机具有诸多优势&#xff0c;但其使用寿命仍可能受到多种因素的影响。 首先&#xff0c;环境因素对哈默纳…

【ESP32 IDF快速入门】点亮第一个LED灯与流水灯

文章目录 前言一、有哪些工作模式&#xff1f;1.1 GPIO的详细介绍1.2 GPIO的内部框图输入模式输出部分 二、GPIO操作函数2.1 GPIO 汇总2.2 GPIO操作函数gpio_config配置引脚reset 引脚函数设置引脚电平选中对应引脚设置引脚的方向 2.3 点亮第一个灯 三、流水灯总结 前言 ESP32…

基于深度学习的苹果叶片病害检测系统(含UI界面、yolov8、Python代码、数据集)

项目介绍 项目中所用到的算法模型和数据集等信息如下&#xff1a; 算法模型&#xff1a;     yolov8 yolov8主要包含以下几种创新&#xff1a;         1. 可以任意更换主干结构&#xff0c;支持几百种网络主干。 数据集&#xff1a;     网上下载的数据集&#x…

UE4 Niagara 关卡1.4官方案例解析

sprites can face the camera&#xff0c;or they can face any arbitrary vector&#xff0c;in this case the vector between the center of the system and the particle itself&#xff08;粒子可以面对摄影机&#xff0c;也可以面对任意向量&#xff0c;在这个实例中的向…