多线程(如何理解pthread库)

上一节,我们主要介绍了pthread库中一些常见函数的用法,这节我们主要分析一下pthread库到底是什么?

什么是库

我们之前提过,在每一个linux平台下,必定会存在对应的pthread库
它存在于/lib64这个路径底下
在这里插入图片描述
换句话说,库的本质其实就是文件
既然是文件,按照我们之前动态库的知识,我们知道,它需要从磁盘,加载到内存之中,再根据页表,映射到对应的共享区
由于我们不同的执行流,共享的是同一个虚拟地址,因此,同一进程的不同线程都可以随时访问库中的代码和数据
在库中的代码,就有包含诸如线程管理的操作,通过这种方式,我们就可以实现对应的线程切换的操作
具体如何切换呢?
先描述再组织
先描述一个线程,再将不同描述好的线程组织起来,才考虑切换的问题

LWP和TCB

和文件系统类似,在内核级别,我们存在一个file的结构体,并通过数组的方式(文件描述符),对这些结构体进行管理
但是我们进行的操作,都是对struct FIL(C语言),或者fstream对象(C++)进行操作
这是因为操作系统并不信任用户,假如放开权限,让用户自己进行管理,一是所耗费的成本实在是太大了,学习系统级别的操作并不容易;二是极度不安全,用户随随便便不合适的操作就可能导致操作系统的崩溃
因此,我们所学习的操作,都是建立在这之上的,语言级别的操作函数,它的底层会调用相应的诸如open等等系统接口函数
无独有偶
我们之前提到过,在linux系统下,并没有真线程的概念
只有赋用进程结构的轻量级进程LWP
LWP就类似于我们内核中的struct file结构体,我们用户是不能直接操作的
真正的系统调用接口函数是诸如clone这样的函数
在这里插入图片描述

linux开发者在这之上,开发了pthread库,通过调用pthread库里面的函数(桥梁),在用户层,我们就可以进行线程的管理;而在操作系统眼里,我们就会调用相应的clone系统调用接口,对轻量级线程LWP进行操作
那在语言层面上进行类比,也应该有类似struct FILE结构体一样的存在
答案是也存在的!
在虚拟内存中(语言层面),我们在共享区会存在对应的struct pthread结构体,我们把它称之为TCB(用户级线程)
在这里插入图片描述

至此,我们就初步实现了描述一个线程
在TCB中,存有对应线程的属性,其中就包括我们先前提到的joinable属性,还有每个线程都存在的自己的独立栈空间等等

线程ID

那如何管理不同的线程呢?
文件系统采用的是文件描述符,以数组的方式进行管理,对应的下标就是文件描述符
线程采取的也是类似的方法
不同的线程经过描述后,其实是挨在一起的,就像一个"数组"一样.
因此我们要找到一个用户级线程,实际上,只需要找到该线程内存块的起始地址,然后就可以获取到该线程的各种信息

所谓的线程ID,就是一个地址数据!!!
它用来标识线程相关属性集合(TCB)的起始地址
更进一步来说,就是偏移地址,通过线程ID,我们就能访问不同的线程TCB!
在这里插入图片描述

独立栈与线程局部存储

在这之前,我们已经验证过,不同的线程,拥有自己的独立栈
假如每个线程都创建一个cnt变量,并在每次循环的时候,进行对应的减减操作,此时虽然变量名是相同的,但是它们并不是同一个变量

    1 #include <iostream>2 #include <pthread.h>3 #include <unistd.h>4 #include <cstring>5 #include <string>6 using namespace std;7 8 int g_val =100;9 string toHex(pthread_t tid)10 {11   char buffer[64];12   snprintf(buffer,64,"0x%x",tid);13   return buffer;14 }15 void* pthreadRun(void* args)16 {17   string name = static_cast<const char*>(args);18   int cnt = 5;19   while(cnt)20   {21     cout << name << " : " << cnt-- << ",cnt: " << cnt << endl;                                                                                                    22     //cout << name << " g_val: " << g_val++ << ", &g_val: " << &g_val << endl;23     sleep(1);24   }25   return nullptr;26 }27 int main()28 {29   pthread_t t1,t2,t3;30   pthread_create(&t1,nullptr,pthreadRun,(void*)"thread 1");31   pthread_create(&t2,nullptr,pthreadRun,(void*)"thread 2");32   pthread_create(&t3,nullptr,pthreadRun,(void*)"thread 3");33 34   pthread_join(t1,nullptr);35   pthread_join(t2,nullptr);36   pthread_join(t3,nullptr);37   return 0;38 }

从输出的结果也可以验证这一点,每个不同的cnt都会进行减1操作
若是相同的cnt,三个线程同时进行减1操作,则很快就会减为0
在这里插入图片描述
而cnt在函数内部,是一个临时变量
也侧面印证了我们所有线程都有自己独立的栈结构的观点
那我们原来说的虚拟地址空间中的栈又是什么呢?
之前我们的说法针对的都是拥有一个执行流的进程
现在也是一样,只不过是多个执行流
因此,我们之前所说的,虚拟地址空间中的栈,是进程系统栈,是主线程用的;而新线程用的是库中的栈
其中,下面的这些数据,都是每个线程自己独立拥有的

线程ID
一组寄存器

errno
信号屏蔽字
调度优先级

更深层次讲,为什么每个线程能够自由的切换不同的栈呢?

原因就在于每个线程都有自己独立的一组寄存器,进入函数,实际就是创建对应的函数栈帧,只要更改ebp,esp,我们就能切换线程的栈

C++线程库

这里并不是讲解C++线程库是如何使用的
而是指出,在使用C++线程库的时候,也需要包含pthread.h头文件
假如不包相应的头文件,则g++编译的时候,就会发生报错
原因就在于C++thread库的实现,其实就是对我们上述讲的对原生线程库的封装,使用户实现多线程更为便捷
而在使用的时候,我们也是多学习有关语言层次的线程库使用,而比较少用原生线程库
原因就在于,语言是可以跨平台实现的,在封装的时候,就已经充分考虑过平台问题,一段相同的代码,既可以在linux上跑,也可以在windows上跑;但是原生线程库,只适用于Linux系统

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

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

相关文章

【从入门到起飞】IO高级流(1)(缓冲流,转换流,序列化流,反序列化流)

&#x1f38a;专栏【JavaSE】 &#x1f354;喜欢的诗句&#xff1a;天行健&#xff0c;君子以自强不息。 &#x1f386;音乐分享【如愿】 &#x1f384;欢迎并且感谢大家指出小吉的问题&#x1f970; 文章目录 &#x1f384;缓冲流&#x1f354;字节缓冲流&#x1f6f8;一次读取…

简单的考试系统

开发一个简单的考试系统&#xff0c;在HTML页面中建立一个表单&#xff0c;通过post方法传递参数。题目类型包括单选题、多选题和填空题&#xff0c;要求程序给出考试成绩。 <!DOCTYPE html> <html> <head><title>question.html</title><met…

【LeetCode热题100】--108.将有序数组转换为二叉搜索树

108.将有序数组转换为二叉搜索树 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。 二叉搜索树的中序遍历是升序…

13链表-简单思路练习

目录 LeetCode之路——876. 链表的中间结点 分析&#xff1a; 解法一&#xff1a;常规思路 解法二&#xff1a;快慢指针 LeetCode之路——876. 链表的中间结点 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回…

竞赛 机器视觉opencv答题卡识别系统

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 答题卡识别系统 - opencv python 图像识别 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满分5分…

互联网Java工程师面试题·ZooKeeper 篇·第一弹

目录 1. ZooKeeper 面试题&#xff1f; 2. ZooKeeper 提供了什么&#xff1f; 3. Zookeeper 文件系统 4. ZAB 协议&#xff1f; 5. 四种类型的数据节点 Znode 6. Zookeeper Watcher 机制 -- 数据变更通知 7. 客户端注册 Watcher 实现 8. 服务端处理 Watcher 实现 9. 客…

ArcGIS Engine:实现Shp/Mxd数据的加载、图层的简单查询

本博客参考&#xff1a;BiliBili UP主 <羊羊旸> &#xff1a; Arcgis Engine学习 目录 01 加载控件以及控件的基本信息等调整 02 编写 <菜单-地图控件> 中各个子工具的代码 2.1 加载Shapefile数据-代码 2.2 加载地图文档数据-代码 2.3 获取图层数量-代码 2.…

vue3 +elementplus | vue2+elementui 动态地通过验证规则子新增或删除单个表单字段

效果图 点击 ‘’ 新增一行&#xff0c;点击‘-’ 删除一行 vue3elementplus写法 template <el-dialog v-model"dialogFormVisible" :title"title"><el-form ref"ruleFormRef" :model"form" :inline"true" lab…

【算法|动态规划No.8】leetcode面试题 17.16. 按摩师

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

对图像中边、线、点的检测(支持平面/鱼眼/球面相机)附源码

前言 图像的线段检测是计算机视觉和遥感技术中的一个基本问题,可广泛应用于三维重建和 SLAM 。虽然许多先进方法在线段检测方面表现出了良好的性能,但对未去畸变原始图像的线 段检测仍然是一个具有挑战性的问题。此外,对于畸变和无畸变的图像都缺乏统一的…

1.2.C++项目:仿muduo库实现并发服务器之时间轮的设计

文章目录 一、为什么要设计时间轮&#xff1f;&#xff08;一&#xff09;简单的秒级定时任务实现&#xff1a;&#xff08;二&#xff09;Linux提供给我们的定时器&#xff1a;1.原型2.例子 二、时间轮&#xff08;一&#xff09;思想&#xff08;一&#xff09;代码 一、为什…

云原生数据库TDSQL-C

数据库系统核心模块 云原生数据库要解决什么问题&#xff1f; HTAP 云数据库VS云原生数据库

mysql双主互从通过KeepAlived虚拟IP实现高可用

mysql双主互从通过KeepAlived虚拟IP实现高可用 在mysql 双主互从的基础上&#xff0c; 架构图&#xff1a; Keepalived有两个主要的功能&#xff1a; 提供虚拟IP&#xff0c;实现双机热备通过LVS&#xff0c;实现负载均衡 安装 # 安装 yum -y install keepalived # 卸载 …

Polygon Miden交易模型:Actor模式 + ZKP => 并行 + 隐私

1. 引言 前序博客&#xff1a; Polygon Miden&#xff1a;扩展以太坊功能集的ZK-optimized rollupPolygon Miden zkRollup中的UTXO账户混合状态模型 Polygon Miden为&#xff1a; ZK-optimized rollup由客户端生成证明完善Polygon ZK系列解决方案&#xff0c;致力于成为网络…

【C语言经典100例题-68】有n个整数,使其前面各数顺序向后移m个位置,最后m个数变成最前面的m个数

方法一 将原数组拆成两部分&#xff0c;前面n-m个数和后面m个数。首先将前面n-m个数逆序&#xff0c;然后将后面的m个数逆序。最后将整个数组逆序即可。 #include <stdio.h>void reverse(int arr[], int start, int end) {for (int i start, j end; i < (start en…

学过的汇编指令整合

1.数据搬移指令 <opcode>{<cond>}{s} <Rd>, <shifter_operand> 解释&#xff1a; <opcode>&#xff1a;指令码 {<cond>}&#xff1a;条件码 {s}&#xff1a;状态位&#xff0c;如果在指令后面加上s&#xff0c;则运算的结果会影响CPSR的条…

傅里叶系列 P2 的定价选项

系列上文&#xff1a;傅里叶系列 P1 的定价选项_无水先生的博客 一、说明 通过本文&#xff0c;您可以了解&#xff1a; 如何从知道什么是傅里叶级数到能够在黑色下为期权定价。 如果您想了解更多关于傅里叶级数基础知识的信息&#xff0c;请查看第 1 部分。随附笔记本&#…

objective-c 基础学习

目录 第一节&#xff1a;OC 介绍 ​​第二节&#xff1a;Fundation 框架 ​第三节&#xff1a;NSLog 相对于print 的增强 ​第四节&#xff1a;NSString ​第五节&#xff1a;oc新增数据类型 第六节&#xff1a; 类和对象 ​类的方法的声明与实现 ​第七节&#xff1a;类…

智慧公厕整体解决方案,厕所革命实施方案的范本

随着城市化进程的不断加快&#xff0c;智慧城市应用正成为未来城市发展的重要方向。其中&#xff0c;智慧公厕作为城市基础设施的重要组成部分&#xff0c;其建设范本已经成为各建设中的智慧城市不可或缺的重要内容。那么&#xff0c;如何打造智慧公厕整体解决方案&#xff1f;…

将数组和减半的最少操作【贪心2】

题目&#xff1a;将数组和减半的最少操作 贪心思路&#xff1a;每次挑选最大的数来减半。 解法&#xff1a;贪心大根堆 class Solution { public:int halveArray(vector<int>& nums) {priority_queue<double> heap;double sum 0.0;for(int& x : nums){hea…