多线程(如何理解pthread库)

news2025/1/11 22:51:51

上一节,我们主要介绍了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.coloradmin.cn/o/1058525.html

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

相关文章

Android逆向学习(五)app进行动态调试

Android逆向学习&#xff08;五&#xff09;app进行动态调试 一、写在前面 非常抱歉鸽了那么久&#xff0c;前一段时间一直在忙&#xff0c;现在终于结束了&#xff0c;可以继续更新android逆向系列的&#xff0c;这个系列我会尽力做下去&#xff0c;然后如果可以的话我看看能…

jvm 参数配置

查看当前jvm配置参数的值 jsp查看所有的jvm端口 jinfo -flag 参数(XX:后面的) JIT配置 -XX:CompileThreshold在方法调用的默认阈值在客户端1500次&#xff0c;在服务器端10000次。 -XX:-UseCounterDecay用来关闭热度衰减。 -XX:CounterHalfLifeTime设置半衰减的时间&#x…

Ubuntu中启动HDFS后没有NameNode解决办法

关闭进程&#xff1a; stop-dfs.sh 格式化&#xff1a; hadoop namenode -format 出现报错信息&#xff1a; 23/10/03 22:27:04 WARN fs.FileUtil: Failed to delete file or dir [/usr/data/hadoop/tmp/dfs/name/current/fsimage_0000000000000000000.md5]: it still exi…

我的企业证书是正常的但是下载应用app到手机提示无法安装“app名字”无法安装此app,因为无法验证其完整性解决方案

我的企业证书是正常的但是下载应用app到手机提示无法安装“app名字”无法安装此app&#xff0c;因为无法验证其完整性解决方案 首先&#xff0c;确保您从可信任的来源下载并安装企业开发者签名过的应用程序。如果您不确定应用程序的来源&#xff0c;建议您联系应用程序提供者…

大数据-玩转数据-Flink 海量数据实时去重

一、海量数据实时去重说明 借助redis的Set&#xff0c;需要频繁连接Redis&#xff0c;如果数据量过大, 对redis的内存也是一种压力&#xff1b;使用Flink的MapState&#xff0c;如果数据量过大, 状态后端最好选择 RocksDBStateBackend&#xff1b; 使用布隆过滤器&#xff0c;…

快速选择排序

"你经过我每个灿烂时刻&#xff0c;我才真正学会如你般自由" 前些天有些无聊&#xff0c;想试试自己写的快排能否过leetcode上的排序算法题。结果是&#xff0c;不用截图可想而知&#xff0c;肯定是没过的&#xff0c;否则也不会有这篇文章的产出。 这份快排算法代码…

请求转发与请求作用域

创建input.jsp页面&#xff0c;通过表单输入学号、姓名后&#xff0c;单击登录按钮&#xff0c;控制转发到FirstServlet对其进行处理&#xff0c;然后通过请求对象的getRequestDispartcher()获得RequestDispartcher对象&#xff0c;将请求转发至SecondServlet&#xff0c;在Sec…

辅助驾驶功能开发-测试篇(2)-真值系统介绍

1 真值系统概述 1.1 真值评测系统核心应用 快速构建有效感知真值,快速完成感知性能评估,快速分析感知性能缺陷。 主要应用场景包括: 1. 感知算法开发验证: 在算法开发周期中,评测结果可以作为测试报告的一部分,体现算法性能的提升。 2. 遴选供应…

九、2023.10.3.Linux(end).9

文章目录 33、简述mmap的原理和使用场景&#xff1f;34、互斥量能不能在进程中使用&#xff1f;35、协程是轻量级线程&#xff0c;轻量级表现在哪里&#xff1f;36、说说常见信号有哪些&#xff0c;表示什么含义&#xff1f;37、说说线程间通信的方式有哪些&#xff1f;38、说说…

基于matlab创作简易表白代码

一、程序 以下是一个基于MATLAB的简单表白代码&#xff1a; % 表白代码 clc; % 清除命令行窗口 clear; % 清除所有变量 close all; % 关闭所有图形窗口 % 输入被表白者的名字 name input(请输入被表白者的名字&#xff1a;, s); % 显示表白信息 fprintf(\n); fprintf(亲爱的…

深入浅出的算法设计与分析技巧解读(软件设计师笔记)

&#x1f600;前言 在计算机科学的庞大体系中&#xff0c;算法始终占据着核心的地位&#xff0c;充当着解决问题的“钥匙”。本章主要深入探讨了算法设计与分析这一主题&#xff0c;旨在通过具体的问题解析和代码实现&#xff0c;引导读者深入理解各种经典算法的设计原理和应用…

ElasticSearch第四讲:ES详解:ElasticSearch和Kibana安装

ElasticSearch第四讲&#xff1a;ES详解&#xff1a;ElasticSearch和Kibana安装 本文是ElasticSearch第四讲&#xff1a;ElasticSearch和Kibana安装&#xff0c;主要介绍ElasticSearch和Kibana的安装。了解完ElasticSearch基础和Elastic Stack生态后&#xff0c;我们便可以开始…

力扣 -- 322. 零钱兑换(完全背包问题)

参考代码&#xff1a; 未优化代码&#xff1a; class Solution { public:int coinChange(vector<int>& coins, int amount) {int n coins.size();const int INF 0x3f3f3f3f;//多开一行&#xff0c;多开一列vector<vector<int>> dp(n 1, vector<i…

cadence - 解决orcad无响应的有效方法

文章目录 解决orcad无响应的有效方法概述笔记备注补充好像必须要在英文(美国)语言的主环境下运行才行补充 - orcad无响应的可能原因补充 - 英文模式也不好使补充 - orcad无响应的真实原因解决orcad无响应的有效方法END 解决orcad无响应的有效方法 概述 在画H7的飞达控制底板.…

复习C语言数组的用法

实验内容 1.1设计一个函数fun&#xff0c;功能是有N*N的矩阵&#xff0c;根据给定的m值&#xff0c;m<N,将每行元素中的值&#xff0c;均往右移m个位置&#xff0c;左边置0 #include<stdio.h> void fun(int (*a)[3],int m){int n,j,i,k,num;int p2;//右移位置列数nu…

Linux:minishell

目录 1.实现逻辑 2.代码及效果展示 1.打印字符串提示用户输入指令 2.父进程拆解指令 3.子进程执行指令,父进程等待结果 4.效果 3.实现过程中遇到的问题 1.打印字符串的时候不显示 2.多换了一行 3.cd路径无效 4.优化 1.ll指令 2.给文件或目录加上颜色 代码链接 模…

MySQL进阶 —— 超详细操作演示!!!(下)

MySQL进阶 —— 超详细操作演示&#xff01;&#xff01;&#xff01;&#xff08;下&#xff09; 五、锁5.1 概述5.2 全局锁5.3 表级锁5.4 行级锁 六、InnoDB 引擎6.1 逻辑存储结构6.2 架构6.3 事务原理6.4 MVCC 七、MySQL 管理7.1 系统数据库7.2 常用工具 MySQL— 基础语法大…

【管理运筹学】第 8 章 | 动态规划(5,设备更新问题)

系列文章 【管理运筹学】第 8 章 | 动态规划&#xff08;1&#xff0c;多阶段决策过程与动态规划基本概念&#xff09; 【管理运筹学】第 8 章 | 动态规划&#xff08;2&#xff0c;动态规划的基本思想与模型求解&#xff09; 【管理运筹学】第 8 章 | 动态规划&#xff08;3&…

Vscode 如何创建java项目,并添加包

创建java项目 添加包 先打开这个资源管理器中的javaProject&#xff0c;然后打开这个javaProject&#xff0c;点击里面的Reference Libraries,然后点击加号 选择要添加的包然后进行确认即可

C/C++学习 -- 分组密算法(3DES算法)

1. 3DES算法概述 3DES&#xff08;Triple Data Encryption Standard&#xff09;&#xff0c;又称为TDEA&#xff08;Triple Data Encryption Algorithm&#xff09;&#xff0c;是一种对称加密算法&#xff0c;是DES&#xff08;Data Encryption Standard&#xff09;的加强版…