Linux:线程概念、理解、控制

目录

一、认识线程

1.认识线程V1

2.认识线程V2

3.认识线程V3

4.认识线程V4

5.认识线程V5

二、线程控制

1.前言

2.创建线程

3.线程等待

4.线程终止

5.线程分离

三、线程理解


一、认识线程

1.认识线程V1

        借用大多数计算机教材的话,线程是进程的一个执行分支,线程是CPU调度的基本单位。

        多进程的缺点在于,每创建一个进程,就要创建一个PCB对象,一份地址空间,一张页表,想办法把这部分消耗缩小,就只需新建一份"PCB",地址空间和页表共用一份,代码部分再均分,比如有5个进程就把所有调度函数均分为5份,如此一来,CPU并不知道自己是执行多进程,只是在调度一个又一个PCB,而用户看来,CPU却是通过“多线程的方式”提高了效率。

2.认识线程V2

        既然如此,多个进程有多个线程,系统中存在大量线程,必然需要描述、组织,然而,Linux下,并没有再单独定义线程结构体,而是用进程的结构体来模拟线程。

3.认识线程V3

  • 重新认识进程

        以前认识进程,这个进程是单执行流,用现在的话说就是内部只有一个线程。

        现在认识进程,这个进程内部有多个线程,多个执行分支。

        总结的看,进程是系统分配资源的单位,而线程是系统调度的单位,因此,往后理解进程,都要站在系统分配资源的角度去看。

4.认识线程V4

  • cpu调度角度

        现在来看,有的进程只有一个执行流,cpu调度可以称为调度进程,而有的进程有多个执行流,cpu调度时称为调度线程。

        Linux下,为了统一这种含糊不清的概念,把cpu调度的执行流统称为轻量级进程。线程<= 执行流 <= 进程。因此,cpu不再区分自己到底是在线程调度还是进程调度,都称为执行流。

  • 内核级虚拟机技术

        我们现在看待进程,它是操作系统分配资源的基本单位,可以认为是一个容器,那么如果一个进程所对应的代码部分是一个操作系统,意味着这个操作系统支持内核级虚拟机技术。

5.认识线程V5

  • 页表

        抛出一个问题,操作系统是怎么给多线程均分代码的?

        第一个结论,内存本质上是有限个4KB的内存块,定义为数组,方便增删查改。

        第二个结论,页表并不是简单的K-V映射,虚拟地址是有划分的,比如32位的虚拟地址,前10位是一个整体,中间10位是一个整体,后12位一般是页内偏移地址,而页表其实是多张页目录和多张页内偏移表。

        第三个结论,多线程划分代码,其实就是让每一个线程拿到自己代码所在的n张页内偏移表。

二、线程控制

1.前言

        ps -aL指令查看LWP(light weight process)

ps -aL

        过去,要么是单进程单执行流,要么是多进程多执行流,而学习多线程后,变成了单进程多执行流。

        在内核层面,cpu调度的是一个又一个的LWP,只有轻量级进程。

        Linux的内核代码没有线程结构体,只有轻量级进程的结构体定义。而在用户层面,要严格区分进程和线程。因此需要对内核的系统调用封装,于是有一个库叫 pthread库,这个库不属于内核代码,但是安装Linux操作系统时必须安装这个库。

2.创建线程

        pthread_create

        通过man手册可以得知,pthread_create这个函数用来创建一个新的线程,使用这个函数需要包含头文件<pthread.h>,由于在3号手册查到了这个函数,因此说明这个函数不是系统调用,是用户层封装的函数。此外,man手册提示编译和链接时要加特定选项-pthread

void* HandleTask(void* args)
{//新线程std::string threadname = (char*)args;
}   int main()
{//主线程pthread_t tid;pthread_create(&tid,nullptr,HandleTask,nullptr);return 0;
}

3.线程等待

        在学习进程时,父进程要等待回收子进程。而线程这快,主线程也要等待新线程。

  • 运行成功的多线程程序,主线程一定是最后运行结束的。主线程退出=进程退出,如果主线程的主体代码运行完毕,而新线程还在运行,则主线程需要等待新线程的执行结果。

        pthread_join

        第二个参数是输出型参数。 

void* HandleTask(void* args)
{//新线程std::string threadname = (char*)args;int cnt =5;while(cnt){sleep(1);cnt--;}return (void*)111;
}   int main()
{//主线程//创建pthread_t tid;pthread_create(&tid,nullptr,HandleTask,(void*)"name");//等待void* ret = nullptr;int cnt =10;while(cnt){sleep(1);cnt--;}int abc = pthread_join(tid,&ret);std::cout << "abc->" << abc << "new thread ret->" << (long)ret << std::endl;return 0;
}

        没有发生异常的情况下,新线程的运行结果是可以通过参数手动获取到的。 如果任何一个线程出现异常(div 0, 野指针),都会导致整个进程退出! 同时说明多线程代码往往健壮性不好。

4.线程终止

  • 线程return
  • pthread_exit

        哪一个线程调用这个函数,哪一个线程就被终止。

pthread_exit((void*)111);
  • pthread_cancel

        这个函数由主线程调用,用来发送终止信号。 

 //终止测试sleep(1);pthread_cancel(tid);

        返回结果为-1,表明不是正常退出。

5.线程分离

        pthread_detach

         

        新线程做线程分离

pthread_detach(pthread_self());

        主线程也可以主动分离掉新线程。

pthread_detach(tid);

  • 线程分离后,到底发生了什么?什么样的情况需要线程分离?

        每一个新线程默认是需要让主线程等待的,即主线程需要调用pthread_join函数。如果主线程不需要关心新线程的执行状态,那么就可以将这个新线程分离。新线程分离后,仍旧和其他线程共享资源,并且保留自己原来的私有资源但是!如果主线程调用pthread_join去等待这个新线程,是会出错的,即被分离的线程意味着不再需要主线程等待。不管怎么设计程序,都建议让主线程最后一个退出。

三、线程理解

  • 多线程相比多进程的优点 

 创建一个新线程的开销要比一个新进程小得多。

与进程的切换相比,线程切换时操作系统所做的操作要少得多。

1.切换线程时,需要切换上下文的寄存器相对少一点,切换线程,只需切换保存是哪一个线程的寄存器,而切换进程,还要多切换用来保持虚拟地址空间、页表的寄存器。

2.由于cpu缓存技术, 加载内存中代码时是一次性加载多行,而切换进程时,前后的代码内容更大概率不是在相邻存储,更大概率可能要刷新缓存,而多线程共享代码,大概率不需要刷新缓存。

  • 线程私有的数据

1.线程的硬件上下文数据,本质是cpu寄存器的值,(这部分数据调度线程)

2.每一个线程都有自己的独立栈结构,(用来保证线程的常规运行)

  • 线程共享的数据

1.代码部分、全局数据

2.文件描述符表、页表、进程地址空间等等

  • C++11的多线程

语言层的多线程,其实是对pthread库的进一步封装。

  • 更深的理解pthread_t

        如何标识一个唯一的线程,Linux下有这样两种设计,在内核一层,即操作系统层面,并没有定义线程结构体,而是定义了一个LWP结构体,名为轻量级进程,等同于线程,内核里面LWP是唯一的。但是,Linux内核对线程的各种操作控制并不同于我们理论上学习的线程操作控制,因此,Linux又做封装,即封装好的pthread库,编译链接多线程程序时,必须要链接这个库,本质是第三方库,因为它既不属于语言,也不属于操作系统调用。

        pthread_t就是pthread库里面定义的概念。

        加载动态库,加载到物理内存后,经页表映射到虚拟地址空间中的共享区,在代码区中执行到pthread_t tid这行代码后,cpu则跳转到共享区(动态库已经加载)对应的定义处执行这行代码。

        pthread动态库对线程做了管理,即定义了线程结构体,也使用数据结构控制。

         

        每一个线程都有对应的结构体,这个结构体有的地方也叫tcb而结构体的起始地址,就是pthread_t tid的值。

        上文提到了线程私有的数据之一,就是独立栈结构。也是在pthread库里面的线程结构体中定义的,因为每一个线程都有对应执行的代码,可能会创建局部变量,这些变量就保存在这部分栈结构中。

        线程局部存储:全局变量归所有线程共有,如果希望只写一份定义全局变量的代码,但是实际上却是多个进程各有一份数据,就可以用像下面这样定义,这些值保存在线程局部存储空间中。

 __thread int IngTime = 0;

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

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

相关文章

maven使用默认settings.xml配置时,Idea基于pom.xml更新依赖时报错,有些组件下载时连接超时

1、问题背景&#xff1a;maven使用默认settings.xml配置时&#xff0c;Idea基于pom.xml更新依赖时报错&#xff0c;有些组件下载时连接超时&#xff0c; 通过日志发下&#xff0c;去连接maven.org网站下载依赖&#xff0c;有时候肯定会超时。 2、解决办法&#xff1a;使用国外…

【第3章:卷积神经网络(CNN)——3.5 CIFAR-10图像分类】

嘿,小伙伴们,今天咱们来聊聊一个超级酷炫的话题——卷积神经网络(CNN)及其在CIFAR-10图像分类中的应用。这不仅仅是一个技术话题,更是一场探索人工智能奥秘的旅程。准备好了吗?咱们这就发车! 一、CNN:人工智能的“千里眼” 首先,咱们得知道CNN是啥。CNN,全名Convol…

Win10环境借助DockerDesktop部署大数据时序数据库Apache Druid

Win10环境借助DockerDesktop部署最新版大数据时序数据库Apache Druid32.0.0 前言 大数据分析中&#xff0c;有一种常见的场景&#xff0c;那就是时序数据&#xff0c;简言之&#xff0c;数据一旦产生绝对不会修改&#xff0c;随着时间流逝&#xff0c;每个时间点都会有个新的…

【射频仿真技巧学习笔记】Cadence修改图表背景、曲线颜色

很多初始设置的Cadence仿真出来的曲线是长下面这样的&#xff0c;背景是黑色&#xff0c;而且曲线是Dot点状fine细线&#xff0c;这样查看图像会很不方便 如果一条一条去改曲线的性质会很不方便&#xff0c; 这里我介绍两种方法 方法一 首先打开virtuoso的初始界面 在命令…

【Vue3源码解析】应用实例创建及页面渲染

下载源码 git clone https://github.com/vuejs/core.git写该文章时的Vue版本为&#xff1a; "version": "3.5.13",这里要注意 pnpm 的版本不能太低&#xff0c;我此时的版本为 9.15.4。更新 pnpm 版本&#xff1a; npm install -g pnpm然后安装依赖&…

Ubuntu 系统 cuda12.2 安装 MMDetection3D

DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 需要更多数据资源和技术解决方案&#xff0c;知识星球&#xff1a; “DataBall - X 数据球(free)” 贵在坚持&#xff01; ---------------------------------------…

云贝餐饮连锁V3独立版全开源+vue源码

一.介绍 云贝餐饮连锁V3独立版&#xff0c;作为一款全开源、全插件的源码部署系统&#xff0c;其在餐饮行业软件系统中独树一帜。该系统不仅功能全面&#xff0c;涵盖了餐饮连锁企业的日常运营、财务管理、库存管理、会员管理等多个方面&#xff0c;而且框架结构清晰&#xff…

learngit git常用指令

learngit & git常用指令 目录 learngit & git常用指令1.add && commit2.status && diff3.log && reset && reflog4.Repositoty(版本库)5.checkout6.rm7.github(push && rm)8.clone9.branch && switch && merg…

算法1-7 搜索

目录 1 深度优先搜索 1.1 P1219 八皇后 1.2 P1135 深搜剪枝 1.3 P1605 多路深搜回溯 2 广度优先搜索 2.1 P1443 马的遍历 3 多方向搜索 3.1 P1101 单词方阵 1 深度优先搜索 需要考虑深度的情况&#xff1a; 固定长度组合&#xff1a;当问题要求生成确定长度的组合&…

响应式布局学习笔记

什么是响应式布局&#xff1f; 响应式布局&#xff08;Responsive Web Design&#xff09;是一种网页设计方法&#xff0c;使网站能够根据设备屏幕尺寸&#xff08;如手机、平板、电脑&#xff09;自动调整内容和布局&#xff0c;提供最佳浏览体验。 如何调试响应式布局&…

Cursor 与团队协作:提升团队开发效率

引言 在团队开发中&#xff0c;代码质量参差不齐、重复错误频发、代码审查耗时过长是制约效率的三大痛点。据 GitHub 调查&#xff0c;开发者平均每周花费 4.3 小时修复他人代码问题&#xff0c;而 60% 的合并请求&#xff08;PR&#xff09;因风格或低级错误被驳回。Cursor 作…

rocketmq-netty通信设计-request和response

1、NettyRemotingServer启动分析 org.apache.rocketmq.remoting.netty.NettyRemotingServer#start public void start() {this.defaultEventExecutorGroup new DefaultEventExecutorGroup(nettyServerConfig.getServerWorkerThreads(),new ThreadFactory() {private AtomicI…

蓝桥杯之图

图&#xff1a; 对于图来说&#xff0c;重点在于之后的最短路径算法&#xff0c;这边简单做一下了解即可 代码&#xff1a; #include<iostream> #include<string> #include<vector> #include<list> #include<queue> using namespace std; clas…

mysql 学习15 SQL优化,插入数据优化,主键优化,order by优化,group by 优化,limit 优化,count 优化,update 优化

插入数据优化&#xff0c; insert 优化&#xff0c; 批量插入&#xff08;一次不超过1000条&#xff09; 手动提交事务 主键顺序插入 load 从本地一次插入大批量数据&#xff0c; 登陆时 mysql --local-infile -u root -p load data local infile /root/sql1.log into table tb…

143,【3】 buuctf web [GYCTF2020]EasyThinking

进入靶场 一开始那个题目名字就想到了框架 扫描目录 访问后自动下载了 找源码 <?php namespace app\home\controller;use think\exception\ValidateException; use think\facade\Db; use think\facade\View; use app\common\model\User; use think\facade\Request; use …

数据守护者:备份文件的重要性及自动化备份实践

在信息化社会&#xff0c;数据已成为企业运营和个人生活的重要组成部分。无论是企业的核心业务数据&#xff0c;还是个人的珍贵照片、重要文档&#xff0c;数据的丢失或损坏都可能带来无法估量的损失。因此&#xff0c;备份文件的重要性愈发凸显&#xff0c;它不仅是数据安全的…

PHP支付宝--转账到支付宝账户

官方参考文档&#xff1a; ​https://opendocs.alipay.com/open/62987723_alipay.fund.trans.uni.transfer?sceneca56bca529e64125a2786703c6192d41&pathHash66064890​ 可以使用默认应用&#xff0c;也可以自建新应用&#xff0c;此处以默认应用来讲解【默认应用默认支持…

vscode插件开发

准备 安装开发依赖 npm install -g yo generator-code 安装后&#xff0c;运行命令 yo code 运行 打开项目&#xff0c; 点击 vscode 调式 按 F5 或点击调试运行按钮 会打开一个新窗口&#xff0c;在新窗口按快捷键 CtrlShiftP &#xff0c;搜索 Hello World 选择执行 右下角出…

win11安装wsl报错:无法解析服务器的名称或地址(启用wsl2)

1. 启用wsl报错如下 # 查看可安装的 wsl --install wsl --list --online此原因是因为没有开启DNS的原因&#xff0c;所以需要我们手动开启DNS。 2. 按照如下配置即可 Google的DNS&#xff08;8.8.8.8和8.8.4.4) 全国通用DNS地址 (114.114.114.114) 3. 运行以下命令来重启 WSL…

mysql 存储空间增大解决方案

一&#xff1a;查询数据库中表占比比较多的表 SELECT table_name AS "Tables", round(((data_length index_length) / 1024 / 1024), 2) AS "Size (MB)" FROM information_schema.tables WHERE table_schema "自己的数据库名"; …