【Linux操作系统】--多线程(一)

目录

Linux线程概念

什么是线程

Linux线程与接口关系的认识

线程的用途

线程和进程共享和私有

线程的优点

线程的缺点

线程异常

进程与线程间的关系

线程控制

创建线程

线程等待

线程终止

分离线程

线程互斥

线程需要互斥原因:

进程间互斥相关背景概念:

模拟线程抢占

对临界区进行加锁:

互斥量实现原理探究

常见的线程安全的情况

可重入和线程安全

线程安全问题

常见重入的情况:

可重入与线程安全联系

可重入与线程安全区别

常见锁概念

死锁

死锁四个必要条件

避免死锁


Linux线程概念

什么是线程

笼统的讲:线程是在进程内部运行的一个执行分支(执行流),属于进程的一部分,粒度要比进程更加细致和轻量化。

  • 在一个程序里的一个执行路线叫做线程,更准确的定义是:线程是”一个进程内部的控制序列“
  • 一切进程至少都有一个执行线程
  • 线程在进程内部运行,本质是在进程地址空间内运行
  • 在Linux系统中,在CPU眼里,看到的PCB都要比传统的进程更加轻量化
  • 透过进程虚拟地址空间,可以看到进程大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流。

Linux线程与接口关系的认识

一个进程内存在多个线程,进程:线程=1:n,那么操作系统需要对这些线程进行管理,根据以往经验,需要先描述再组织。所以线程也需要有自己的控制块TCB,因为Linux操作系统是C语言为基础写的,所以管理TCB控制块是一个结构体struct tcb{},以上是常规OS的做法。

但是实际上因为再写一个进程控制块来管理进程是相当麻烦的,所以只创建进程的task_struct,线程和进程共享一个地址空间,当前进程的资源(包括代码+数据),划分为若干份,让每个PCB使用。

CPU此时看到PCB<=以前所了解的PCB概念,一个PCB就是一个需要被调度的执行流,Linux中没有专门为线程设计TCB,而是用进程的PCB来模拟线程。此时,我们不用维护复杂的进程和线程的关系,不用单独为线程设计任何算法,直接俄使用进程的一套相关的方法。OS只需要聚焦在线程间的资源分配上就可以了。

所以最开始提到的线程在进程内部运行,内部指的是线程在进程的地址空间内运行;线程是一个执行分支:指的是CPU调度的时候,只看PCB,每个PCB曾经被指派过的方法和数据,CPU可以直接调度。

之前讲到的进程,内部只有一个执行流的进程;今天讲到的进程,内部可以具有多个执行流。众所周知,创建进程的”成本“非常高,成本包括时间+空间。创建进程要使用的资源也非常多。从内核的视角来看:进程是承担分配系统资源的基本实体,线程是CPU调度的基本单位,承担进程资源一部分的基本实体。也就是说进程划分资源给线程。

Linux因为是用进程模拟的,所以Linux下不会直接给我们直接提供线程的接口,而是给我们提供,在同一个地址空间内创建PCB的方法,分配资源给指定的PCB接口。这样不直接提供接口的方式对程序员用户非常不友好,那么用户得自己实现,来创建线程的接口,释放线程,等待线程等等这些接口。所以系统级别的工程师,在用户层对linux轻量级进程接口进行封装,给我们打包成库,让用户直接使用库接口,也就是原生线程库(用户层)。这个库接口就是我么接下来要学习的库接口(pthread)。

线程的用途

线程和进程共享和私有

所有的轻量级进程(也可能是”线程“)都是在同一个进程地址空间内运行的。

进程是具有独立性的,但也可以有部分共享资源(通过管道,ipc资源实现)。线程,大部分资源是共享的,可以有部分资源是”私有“的(如:pcb,栈和上下文)。

栈的私有性体现在,一个线程的产生的临时数据需要压栈入栈的,如果共享栈,栈中数据混淆,线程之间的数据混在一块,非常麻烦,所以栈是有独立性的。因为一个线程是cpu调度的基本单位,这样的话每个线程pcb块需要有自己调度的上下文。

私有:细化分一下进程私有资源:

  • 线程ID
  • 一组寄存器
  • errno
  • 信号屏蔽字
  • 调度优先级

共享:进程的多个线程共享同一个地址空间,因此TEST segment,Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用;或者定义一个全局变量,在各个线程中都可以访问到;除此之外,各线程还共享一下进程资源和环境:

  • 文件描述符表
  • 每种信号处理方式(SIG,IGN,SIG_DFL或者自定义信号处理函数)
  • 当前工作目录
  • 用户id和组id

线程的优点

  1. 从创建线程来看,创建一个新线程的代价要比拆功能键一个新进程小的多。不用创建PCB,不用创建地址空间,不用加载代码和数据,只需要分配资源就可以了,就是进程创建PCB分配给你就可以了。
  2. 与进程切换相比,线程之间切换需要OS做的工作相对比较少。进程间切换直接切换上下文,不需要切换页表,不需要更新各种缓存,因为线程之间数据都是有效的。
  3. 线程占用的资源比较少,因为线程不是主要申请资源的角色,只需要用别人创建好的即可
  4. 能充分利用多处理器的可并行数量。
  5. 在等待慢速I/O操作结束的过程中,程序可执行其他的计算任务。
  6. 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现。计算密集型应用最常见的情况有:加密,大数据运算等---主要使用的是CPU资源。
    1. 那么计算密集型是否是线程越多越好呢?不一定!如果线程太多,会导致线程被过度调度切换,调度切换是有成本的。假如有200个线程来操作一个200G进程,一个进程1G,这样的话本来一个线程直接就能完成的操作,200个进程中有大量时间来进行切换调度,使得运行成本变高,反而导致运行效率降低。
  7. I/O密集型应用,为了提高性能,将I/O操作重叠,线程可以同时等待不同的I/O操作。这种应用具体实现有:网络下载,云盘,ssh,在线直播,看电影----需要内存和外设的IO资源。

        cpu+IO密集型这样两方面都用到的应用,最常见的有网络游戏。农药,撸啊撸这些的。

        IO密集型是否线程越多越好呢?IO密集型线程多几个是好的,因为IO操作大部分时间是用来等待的,比如果QQ发送信息,IO大部分时间都是来等待用户输入的。在这等待的过程中,多个线程一起等待,导致了IO操作重叠。但是也并不是IO越多越好,线程太多,线程间来回切换调度,OS是忙不过来的。

线程的缺点

  1. 性能损失
    • 如计算密集型线程的数量比可用的处理器多,那么可能有较大的性能损失,这里的性能损失指的是增加了额外的调度和同步数据的开销,而可用资源不变。
  2. 健壮性降低
    • 编写多线程需要更全面更深入的考虑,在一个多线程程序里,因为时间分配上的细微偏差,或者因为共享了不该共享的变量造成不良影响的可能性很大,换句话说线程之间是缺乏保护的。
  3. 缺乏访问控制
    • 进程是访问的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响
  4. 变成难度提高
    • 编写与调试一个多线程程序比单线程程序难得多。

线程异常

  • 单线程如果出现除零或野指针问题导致线程崩溃,进程也会跟着崩溃。
  • 因为进程具有独立性,导致其他进程最多只是对该进程只读但是不能写。而线程共用的是一个进程的地址空间,线程与线程之间的数据可以互相访问,当一个线程数据出错了,操作系统对该线程发信号,发信号只能发送给该线程对应的进程,进程跟着崩溃了,导致进程内的所有数据被释放,该进程内的其他线程也跟着销毁了(因为线程的数据是进程给的)。所以一个线程崩溃就会导致整个进程崩溃,这也造成了线程的健壮性降低的原因。

进程与线程间的关系

线程控制

创建线程

创建线程使用的接口时pthread_create,用man手册来查看一下,man pthread_create,发现pthread_create使用的是三号手册,头文件是pthread.h,在编译的时候需要引入链接-pthread这个库,这个就是引入第三方库。

pthread_create各个参数的意义:

返回值:创建成功返回0,创建失败返回错误码,告诉我们为什么失败。

pthread_create这个函数的参数:pthread_t是一个无符号整数,第一个叫线程id,用来返回新创建线程的线程id;

第二个线程属性设置成NULL,这里一般的设置有:栈,优先级等线程的属性,一般我们不设置线程属性,第一是我们不懂,第二我们不会轻易设置线程属性,只有OS是最懂得,直接交给OS即可;

第三个是回调函数,这个回调函数意味着你要执行线程中代码的哪一部分;

第四个参数就是要给这个回调函数传入的参数。这个回调函数的返回值和参数的返回值都是void*,所以我们在传入函数和参数的时候需要将它们都强转为void*类型。

 我们来写一个简单的线程函数来验证一下:

main函数中执行主线程,创建了一个新线程thread_run函数,传入自己创建的线程id:tid,线程属性设置为NULL,创建从线程执行函数thread_run,传入函数参数为thread 1,因为thread1 是字符串,需要将它转换为void*类型。在主线程执行循环体的同时,从线程也执行循环体。

[wjy@VM-24-9-centos thread]$ cat mythread
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值