当前位置: 首页 > news >正文

【Linux】初始线程

目录

🌟一、线程概念

🌟二、pthread原生线程库

🌟三、线程 VS 进程

3.1 线程切换轻量化原理

3.2 线程私有数据

3.3 线程优缺点

1)、线程优点

2)、线程缺点

🌟四、线程相关函数及功能

4.1 线程创建--create

4.2 获取自身线程id--self

4.3 线程取消--cancel

4.4 线程终止--exit

4.5 线程等待--join

4.6 线程分离--detach

🌟五、完结



🌟一、线程概念

      一个进程需要访问的大部分资源,如代码,数据,new\malloc 开辟的空间数据,命令行参数,环境变量等,甚至是系统调用的内核代码。进程都是通过虚拟地址空间来访问的,也就是进程地址空间,换而言之,进程地址空间就是进程的资源窗口

     进程的创建是复杂的,首先要创建进程的PCB,进程地址空间,页表,再将进程自身的代码和数据换入内存中,并进行虚拟地址和物理地址的映射。接着将进程的状态改为R运行状态,再把进程的 PCB 挂在运行队列中排队。

      但如果现在已经存在一个进程了,我们把这个进程的PCB复制多份,然后让所有 "进程" 的 PCB 全都指向同一个虚拟地址空间,然后通过技术手段,将这份虚拟地址空间合理分配给每个"进程",当 CPU 调度该 "进程" 时,每个 "进程" 只会调用其中一部分的代码和数据,也就是执行我们所要完成的一小部分。 这样既不影响最终结果,也会减轻单个进程的负担,相当于把一个任务拆分成几个小任务完成。

      我们把这种 "进程" ,比传统进程更轻量化的进程,叫做线程

      线程是在进程内部执行,比进程更加轻量化的一种执行流!


🌟二、pthread原生线程库

     在Linux中,如果Linux真的支持线程,那么就会存在大量的线程,操作系统要对这些线程进行管理,那就是进行 先描述,再组织 。即操作系统要创建一个结构体struct TCB,这样就可以将这些线程用链表的形式管理起来。

     1、 线程又是进程内部的一种执行流,所以要将线程和进程进行耦合。

     2、 线程同样也需要被调度,所以对线程的调度有属于自己的调度算法。


     进程的 PCB 和线程的 TCB 所使用的属性其实是差不多的,都需要pid、状态、调度优先级…并且两者都是需要被调度被运行的,所以其实可以用PCB来代替TCB。

     PCB包含操作系统管理进程所需的各种信息,将其用于线程管理时,可把线程视为轻量级进程这样能以统一的视角看待进程和线程。当操作系统进行线程调度时,依据PCB中的优先级字段,选择优先级最高的线程执行,就如同对进程的调度一样。

     我们将这种直接创建PCB,然后让所有PCB指向同一个地址空间,在对资源进行分配的线程实现方式称为Linux中线程的实现方案!!我们也将这种线程称为轻量级进程!!


     在Linux中没有真正意义上的线程,Linux是用轻量级进程来充当线程。但这是Linux OS自身的独特设计,对于我们用户来说,只认识进程和线程。

      所以我们在Linux中封装了一层软件层。该软件层向下调用轻量级进程的相关接口,向上为用户提供线程的控制接口,这层软件层叫做 pthread 原生线程库,所以我们在使用线程时,需要主动链接pthread库。

     可以通过 ps -al 来查看系统线程


🌟三、线程 VS 进程

      1、 线程是进程内部的一种执行流,线程比进程更加轻量化

      2、线程是CPU调度的基本单位;而进程是资源分配的基本单位

      3、进程拥有独立的进程地址空间和页表, 而线程是共享进程的进程地址空间,页表,和其他资源

      4、进程的栈是由进程地址空间维护的,而线程的栈是由pthread维护,映射到共享区的

声明:共享区是虚拟地址空间的一段区域,共享区通过虚拟地址隔离使线程的物理内存完全独立


3.1 线程切换轻量化原理

     CPU在调度执行某一行代码时,计算机会将该行周围的代码全部加载到CPU cache缓存中。而CPU需要访问内存时,优先访问的是缓存。如果数据在缓存中,访问速度会非常快,如果不在,那就从较慢的主内存中加载。

      而cache缓存是根据虚拟地址来快速查找数据的,一旦进程切换,那么同一个虚拟地址对应的物理地址就会不一样,所以需要重新对数据进行热加载,即将数据加载到cache缓存中;

     但对于线程切换来说,下一个线程极有可能还是访问的这些代码和数据,也就是说不需要更改虚拟地址,因为资源所在的物理地址是不变的!

      除此之外,在CPU中还存在很多寄存器来存储进程/线程的上下文,存在一些寄存器保存的内容执行地址空间和页表。对于线程来说,地址空间和页表是相同的,这也意味着线程切换时我们不需要将所有线程的寄存器全部切换走;只需将少部分保存临时数据的寄存器切换即可! 

那么轻量化的方面就体现出来了->:

  1. 需要切换的寄存器少!
  2. 不需要重新更新cache!

3.2 线程私有数据

     因为线程是进程的内部执行流,所以线程和进程共享大部分数据,如代码段,数据段,全局数据等都是共享的。除了这些,还包括以下资源

     1、共享当前进程的文件描述符表

     2、每种信号的处理方式

     3、当前工作目录

     4、用户id和组id

当然,线程也会有自己私有的数据,具体如下->:

     1、线程拥有独立的寄存器硬件上下文。即当线程被切换出去时,操作系统会把寄存器中的值(上下文)保存到该线程专属的数据结构中,因为线程是被独立调度的,所以每个线程必须拥有独立的上下文,比如运行中产生的临时数据。

     2、线程都有独立栈结构。一般地址空间中的栈属于主进程,然后通过在堆上申请空间,充当其他新线程的栈空间

      3、线程ID、信号屏蔽字、调度优先级、errno等(注:errno是一个全局变量,通过errno可以确定错误的类型)


3.3 线程优缺点

1)、线程优点

      1、线程的创建,删除,切换比进程更加简单轻量,线程占用的系统资源比进程更少

      2、对于计算密集型应用,线程可以利用多处理器并行的特点,将计算分解成多个线程并行处理,提高效率

      3、对于I/O密集型应用,可以将I/O操作进行重叠,以线程同时等待不同的I/O操作,提高性能!

2)、线程缺点
  1. 如果线程的数量比处理器的物理核心多,相当于增加了额外的同步和调度开销,而可用的资源不变,导致性能损失!(CPU的物理核心不是无限的)
  2. 线程会导致整体程序的健壮性降低。如果存在一些错误导致线程异常,会导致进程整体异常退出!
  3. 线程缺乏访问控制!进程是访问控制的基本粒度。对于线程,OS中并没有提供相关的方法进行线程访问控制!
  4. 编写难度高!线程出现问题,通常是很难进行排查的。

🌟四、线程相关函数及功能

4.1 线程创建--create

【函数接口】:

 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);Compile and link with -pthread//编译时,需要链接pthread原生线程库返回值:成功返回0;失败返回错误码

      thread:输出型参数,返回线程id。
      attr:设置线程属性,为NULL表示使用默认属性。
      start_routine:新线程执行方法的函数地址。
      arg:传给start_routine函数的参数。
      错误检查:传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。

【实例】:

void* ThreadRoution(void* arg)
{std::cout << "I am new thread" << std::endl;const char* threadname = (const char*)arg;std::cout << threadname << std::endl;
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, ThreadRoution, (void*)"thread-1");std::cout << "I am main thread" << std::endl;sleep(1);return 0;
}

4.2 获取自身线程id--self

 pthread_t pthread_self(void);

4.3 线程取消--cancel

  int pthread_cancel(pthread_t thread);返回值:成功返回0;失败返回错误码

4.4 线程终止--exit

      线程退出有3种方式:return返回、pthread_cancel取消线程、pthread_exit()线程终止。(不能调用exit()函数,该函数用于进程退出,一般调用,所以相关线程将全部退出!)

【线程终止函数原型】:

 void pthread_exit(void *retval);

      retval:retval不要指向一个局部变量。
      返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)。
      pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,即形参retval必须是全局的或者是malloc分配的。不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时会发现线程函数已经退出了,就是意思这个栈区的指针已经销毁了,那就是野指针的问题了。

4.5 线程等待--join

      一般情况下,主线程需要对新线程进行等待,否则新线程就会一直占用资源,会导致类似于进程的“僵尸”问题,导致内存泄漏!

 int pthread_join(pthread_t thread, void **retval);thread:需要等待的线程idretval:指向待等待线程的返回值

4.6 线程分离--detach

      默认情况下,新创建的线程是 joinable 的,线程退出后,需要对其进行 pthread_join 操作,否则无法释放资源,从而造成系统泄漏。

      如果不关心线程的返回值,join 是一种负担,这个时候,我们可以将线程设置为分离状态,告诉系统,当线程退出时,自动释放线程资源!
      线程组内其他线程对目标线程进行分离,也可以是线程自己分离!但 joinable 和分离是冲突的,一个线程不能既是 joinable 又是分离的!

 int pthread_detach(pthread_t thread);
  1. 线程分离后是可以cancel的,但不能join ! !

🌟五、完结

http://www.dtcms.com/a/477345.html

相关文章:

  • WSL SSH 服务器一站式配置教程
  • 网站建设企业资质等级做游戏模板下载网站有哪些
  • C++ bit级别的复制
  • 《算法闯关指南:优选算法--二分查找》--17.二分查找(附二分查找算法简介),18. 在排序数组中查找元素的第一个和最后一个位置
  • 浏览器原理之详解渲染进程!
  • JSON衍生:JSON5、JSONL、JSONC、NDJSON、BSON、JSONB、JSONP、HOCON
  • 【Java Xml】dom4j写入XML
  • F024 vue+flask电影知识图谱推荐系统vue+neo4j +python实现
  • C++设计模式_结构型模式_外观模式Facade
  • 第 7 篇:交互的乐趣 - 响应用户输入
  • 解决Chrome 140以上版本“此扩展程序不再受支持,因此已停用”问题 axure插件安装问题
  • 如何在火语言中指定启动 Chrome 特定用户配置文件
  • 轻松测试二维码生成与识别:使用Python的qrcode、opencv和pyzbar库
  • 清河做网站报价大背景 网站
  • 迅捷视频转换器 v18.4.23 图文安装教程|支持MP4、AVI、MKV等多格式视频转换
  • 【AI论文】通过渐进式一致性蒸馏实现高效的多模态大语言模型
  • 怎么查看网站开发人网站建设流程及相应技术
  • kubecm切换k8s集群工具
  • Azure多项目管理全攻略:从资源部署到成本分析与优化
  • 怎么做游戏试玩网站城乡建设部网站 挂证
  • 历劫波,明真我——Debug Commune
  • Vue.js 模板语法
  • Spark RDD 宽窄依赖:从 DAG 到 Shuffle 的性能之道
  • scRNA-seq还是snRNA-seq,如何选择
  • 中国人做的比较好的shopify网站慈溪市住房和城乡建设局网站
  • 德州网站建设费用宁国市有做网站
  • 顺德品牌网站建设咨询建设宠物网站的可行性
  • 网站的惩罚期要怎么做苏州网站建设优化
  • 给公司做网站需要什么信息淘宝网站怎么做的好
  • 网站硬件需求淘客网站代理