0


Linux多线程

线程概念

线程的优缺点

线程控制

线程互斥

线程同步

死锁

生产者——消费者模型

线程池

单例模式

读者——写者模型

挂起等待特性的锁与自旋锁

线程概念

线程(一般教材):是再进程内部运行的一个执行分支(执行流),属于进程的一部分,粒度要比进程

更加细和轻量化

常规os对线程的管理,比如Windows

一个进程内可能存在多个线程,即进程:线程 = 1:n,那么os就需要管理这些线程,管理方式:

先描述,再组织,那么线程就要有进程控制块TCB,struct tcb{}

Linux对线程的管理

Linux中没有专门为线程设计TCB,而是用进程的PCB来模拟线程,好处就是维护复杂的进程和线

程之间的关系,不用单独为线程设计任何算法,直接使用进程的一套相关的方法,os只需要聚焦

在****线程间的资源分配上就可以了!

如下图,Linux中,创建线程,就只创建task_struct,共享同一个地址空间,当前进程的资源(代

码+数据),划分为若干份,让每个PCB使用,一个PCB就是一个需要被调度的执行流!

之前的进程,内部只有一个执行流,现在的进程,内部可以具有多个执行流

创建进程的"成本"非常高,成本:时间+空间,要使用的资源是非常多的(0->1)

内核视角:

进程是承担分配系统资源的基本实体!

线程是CPU调度的基本单位,承担进程资源的一部分的基本****实体!

Linux进程,也被称为轻量级进程!!!

Linux线程于接口关系的认识

Linux因为是用进程模拟线程,所以Linux下不会给我们提供直接操作线程的接口,而是给我们提

供,在同一个地址空间内创建PCB的方法,分配资源给指定的PCB的接口,所以对用户特别不友

好!,用户:系统级别的工程师

系统级别的工程师,在用户层对Linux轻量级进程接口(创建线程的接口,释放线程的接****口,等待

线程的接口等等进行封装****,给我们打包成库,让用户直接使用库接口,原生线程库(用户层)

线程和进程共享私有

所有的轻量级进程(可能是"线程")都是在进程的内部运行(地址空间:用来表示进程所能看到的大部

分资源!)

进程:独立性,可以有部分共享资源(管道、ipc资源)

线程:大部分资源是共享的,可以有部分资源是"私有"的(PCB,栈,上下文)

验证

如下图,两个线程打印出来的pid是一样的,证明是同一个进程,但进程内部一定有两个执行流,

否则无法两个死循环都能执行!

L:查看轻量级进程,LWP,是线程id,而下图中的两个线程LWP是不一样的,而进程pid一样,

也能证明这个进程有两个线****程

注意:Linux中,os调度的时候,看的是LWP,不是PID!!!

如何理解我们之前单独一个进程的情况?

PID == LWP

线程的优缺点

优点

创建一个新线程的代价要比创建一个新进程小得多

与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多

线程占用的资源要比进程少很多

能充分利用多处理器的可并行数量

在等待慢速I/O操作结束的同时,程序可执行其他的计算任务

计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现

比如加密,大数据运算等,主要使用的CPU资源,线程也不是越多越好,线程太多会导致线程间被

过度调度切换(有成本的)

I/O密集型应用,为了提高性能,将等待I/O就绪时间重叠,线程可以同时等待不同的I/O操作

比如网络下载,云盘,ssh,在线直播,看电影等,使用的是内存和外设的IO资源,线程也不是越

多越好,不过,IO允许多一些线程,因为大部分时间是在等待IO就绪

另外还有CPU+IO密集型这样的应用:比如网络游戏!

缺点

性能损失

健壮性降低

比如在一个进程里,因为一个线程出现问题,整个进程都可能会出问题

缺乏访问控制

比如一个线程会对另一个线程的数据进行误修改

编程难度提高

线程用途

合理的使用多线程,能提高CPU密集型程序的执行效率

合理的使用多线程,能提高IO密集型程序的用户体验(比如下载某个电影时,边看边下载)

进程与线程的关系

线程控制

线程相关的函数接口

如下图,是创建线程的函数接口,参数thread,是要创建的线程的id,pthread_t在Linux中是无

符****号长整形,参数attr是线程的属性,使用时只需要传NULL即可!start_routine是线程要执行的

方****法,arg是线程待执行方法的参数,另外,编译时还需要在后面添加pthread库

如下图,是创建线程的代码,根据id可看出确实创建了一个新的线程!

创建多个线程

如下图,当线程3崩溃的时候,其它线程也全部都崩溃了,证明线程的健壮性不强!!!

线程等待

一般而言,进程也是需要被等待的,如果不等待,可能会导致类似于"僵尸进程"的问题!

如下图,thread是要被等待的线程的id,retval是以一个输出型参数,用来获取新线程退出的时

候,****函数的返回值,类似于进程等待中的退出码!

验证如下图,不要认为返回值只能是整数,也可以是其它变量,对象的地址(不能是临时的)

代码异常,不需要pthread_join去管,因为这是属于进程去做的事!

注意:多个线程被等待,只能是一个一个地被等待,不能被同时等待!!!

线程终止

函数中return(a.main函数退出return的时候代表(主线程and进程退出) b.其他线程函数return,只

代表当前线程退出)

新线程通过pthread_exit终止自己(exit是终止进程,不要在其他线程中调用,如果你就只想终止一

个线程的话!)

如下图,retval是一个输出型参数,用来返回线程退出信息

取消目标线程

如下图,参数thread是要被取消的线程的id,线程退出的信息是-1

如下图,取消主线程后,子线程还在跑,不建议这样做!

线程分离

如果不想等待,就可以线程分离,分离之后的线程不需要被join,运行完毕之后,会自动释放Z,

pcb,主线程不退出,新线程处理业务完毕再退出,线程分离就相当于同一个屋檐下的陌生人

如下图,参数thread,是要被分离的线程的id

如下图,一个进程被设置为分离之后,绝对不能再进行join了!!!

LWP与线程id

如下图,我们查看到的线程id是pthread库的线程id,不是Linux内核中的LWP,pthread库的线程

id是一个内存地址(虚拟地址)!

如下图,每个线程都要有运行时的临时数据,每个线程都要有自己的私有栈结构!!还要有描述线

程的用户级控制块

如下图,只要有一个上面的,就一定有一个PCB存储LWP,LWP用来获取CPU资源,因为有太多

**的PCB,所以struct pthread中一定包含LWP来找到对应的PCB **

pthread库,用户层的描述线程的属性和数据结构!!!

因为多个线程是共享地址空间的,也就是很多资源都是共享的

优点:通信方便

缺点:缺乏访问控制

线程安全

因为一个线程的操作问题,给其他线程造成了不可控,或者引起崩溃,异常,逻辑不正确等现象

创建一个函数没有线程安全问题的话,不要使用全局,stl,malloc,new等会在全局内有效的数

据(如果要使用,就需要进行访问控制)

尽可能使用局部变量,因为线程有自己的独立栈结构!!!

线程互斥

临界资源:凡是被线程共享访问的资源都是临界资源(多线程打印数据到显示器【临界资源】)

临界区:我的代码中访问临界资源的代码

对临界区进行保护的功能,本质:就是对临界资源的保护,方式:互斥或者同步

互斥:在任意时刻,只允许一个执行流访问某段断码(访问某部分资源),就可以称之为互斥!

比如:printf("hello world") -> lock;printf("");unlock;->一个事情,要么不执行,要么执行完

毕,原子性

如下图,是一个抢票逻辑,但最终却出现了负数,是不符合实际的!显然出现了线程安全问题!

解释

tickets--:不是原子的!在汇编级别它是多行的代码

如下图,当A线程刚执行完第一步,就被切走了,保留着它的上下文,也就算1000,B线程开始执

行,而B线程的竞争力强,当它被切走时,票就只10张了,保留着它的上下文10,内存中的****数据值

也变为了10,这时A线程带着它的上下文继续执行,1000被减到了999,内存中的数据值变****为了

999,这就给其它线程带来了干扰,也就造成了本来只有1000张票,却可以卖出1000多张票。

比如只有1张票了,而第一个线程在if(tickets > 0)检测完后,就被切走了,第二个线程将其减为

0,****这时又到第一个线程执行,就会将其减少为负数

解决方法

对临界区进行加锁,这里采用一个类对其进行封装的方式,也能用全局变量的方式定义锁和票变量

将锁定义为类的静态成员的方式,用一个宏对其初始化

要想线程安全,就必须首先保证锁是安全的,所以加锁和解锁是原子的,原理如下图

提供了swap或change指令->用一条汇编,完成内存和CPU内存寄存器数据的交换!

当CPU执行线程A的代码的时候,CPU内存寄存器内的数据,是线程A私有的!它是执行流的上下

mutex的本质:其实是通过一条汇编,将锁数据交换到自己的上下文中!

当某个线程进入到临界区了之后,当它的时间片结束了之后,也是完全有可能被切走的,这时要做

上下文保护,锁资源也是在上下文中的!而其它线程,在此期间,休想申请锁成功,休想进入临界

区!!!

站在其它进程的视角,对其它线程有意义的状态:A线程要么没有申请,要么线程A使用完锁——

线程A访问临界区的原子性!!!

注意:不能一个线程加锁,另一个线程不用加锁,为了保证临界区的安全,必须保证每个线程都必

须遵守相同的"编码规范"(A 申请锁,其它线程的代码也必须要申请)

注意:加锁只是保护其临界资源在此期间不能被其它线程访问,不能保证该进程会连续执行代码!

可重入与线程安全区别

可重入函数是线程安全函数的一种

线程安全不一定是可重入的,而可重入函数则一定是线程安全的

如果将对临界资源的访问加上锁,则这个函数是线程安全的,但如果这个重入函数若锁还未释放则

会产生死锁,因此是不可重入的,如下图,当进程刚获得锁后,信号递达,再次插入节点,该进程

又要获取锁,但此时锁其实已经被自己拿走了,就无法继续执行,造成死锁问题

死锁

概念:死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用

不会释放的资源而处于的一种永久等待状态,比如有A,B两个小孩,各自有5毛钱,但是买一根棒

棒糖需要1块钱,但两人都不肯把自己的5毛钱给对方,处于僵持状态

只有一个执行流,一把锁的时候,也可能会造成死锁,如下图

死锁四个必要条件

互斥条件:一个资源每次只能被一个执行流使用

请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放****,比如A,B两个小

孩对自己所拥有的5毛钱紧紧握着的同时,还想要对方的5毛钱

不剥夺条件:一个执行流已获得的资源,在末使用完之前,不能强行剥夺,比如A比B长得壮实一

些,A说,你不给我,我就打你,但是因为不允许打架,A也只能动口不能动手,无可奈何

循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系,如下图,比如3个人各有

1把锁,各有1把钥匙,2的锁需要1来解开,3的锁需要2来解开,1的锁要1来解开

避免死锁

破坏死锁的四个必要条件

加锁顺序一致

避免锁未释放的场景

资源一次性分配

线程同步

同步:一般而言,让访问临界资源的过程在安全的前提下(一般都是互斥and原子的),让访问资

源具有一定的**顺序性(具有合理性)!**

条件变量

一般而言,只有锁的情况下,我们是很难知道临界资源的状态!所以引入了条件变量

如下图,是让线程在条件变量下等待的接口

如下图,是唤醒在条件变量下等待的线程的接口

如下图,cond就是一个条件变量,创建了两种线程,ctrl和work,ctrl线程控制work线程,让它

定期运行,当轮到某一个work线程执行时,就唤醒它,其它的就在条件变量下等待,这些work线

程会以队列的形式等待被唤醒,被执行完的就会去后面排队!所以work线程执行的顺序是周期性

的,后面的顺序一定会与开始的顺序一致,而条件变量内一定有一个等待队列,struct code{

int** status; task_struct* q;};**

如下图是一次性唤醒多个线程的接口

生产者——消费者模型

例1:函数调用,函数Func1调用Func2,将自己内的参数传给Func2,而Func2在代码执行完毕

后,将返回值返回给Func1,由此可知,函数和函数之间交互的本质:其实也是数据通信!它是串

行运行的!

通过多线程,是有可能让函数Func1和函数Func2并行运行的!

例2:超市交易

如下图,是三方的关系图,超市的作用:1、能够收集需求,减少交易成本,提高效率;2、将生产

环节和消费环境进行了"解耦"(比如当供货商出现某些问题无法供货时,超市还有存货,不影响消

费****者消费)

注意:超市本身就是一份临界资源!!!

"321"原则

注意:"321原则"只是为了便于记住并理解下面所述的内容!!!

"3" 种关系

供货商与供货商是一种竞争关系,也就是互斥

消费者与消费者是一种竞争关系,互斥

比如当超市只有你和另外一个人都要买某包零食时,此时只有一包了,你们俩都看见了,谁先抢到

就是谁的,也就存在竞争!

供货商与消费者是一种互斥与同步的关系

比如当你在超市一个柜子买一瓶水时,此时供货商要来上货,同时还要涨价时,针对谁先谁后,也

就存在竞争关系,而你要在超市买某种东西时,得供货商供货后,即有货你才能买,也就存在同步

关系!!!

"2" 种角色

生产者(n)和消费者就是两个执行流

"1" 个交易场所

超市(交易场所):一段缓冲区(内存空间、stl容器等)

基于BlockingQueue的生产者消费者模型

首先定义一个命名空间,防止与库里的命名发生冲突,然后创建一个block_queue的类来进行封

装,然后声明了5个变量,两个条件变量_is_empty、_is_full,来让生产者和消费者知道队列是否

空了和是否满了,以便各自执行自己的任务,_Cap来表示队列的最大容量,而在类外定义的常变

量来为其赋值,便于修改,队列,用来插入或者删除数据,以达到生产商品和拿出商品的目的!还

有一个锁变量,来维护线程安全!

注意:只有消费者知道,生产者应该什么时候生产,同样,只有生产者知道,消费者什么时候可以

消****费!

创建构造函数对变量进行初始化,队列无需初始化,以及析构函数来清理资源

生产商品

线程到来,先让其拿到锁,然后判断是否队列满了,满了就不能再生产,而是让其在_is_empty条

件****变量下等待!这里不用if,而用while,是因为防止两个情况发生,一个是线程挂起失败,另一个

是线程被伪唤醒!用if的话,可能会生产条件不具备

调用的这个函数有两个作用,

其一是会首先自动释放_mtx,然后再挂起自己

其二是返回的时候,会首先自动竞争锁,获取到锁之后,才能返回,让线程执行后面的代码

然后插入数据,同时还要唤醒消费者线程,以及解锁,唤醒与解锁的顺序可颠倒,唤醒在前,解锁

在后的话,可能被唤醒了的时候,锁就已经释放了;也可能锁没有释放,这时,可能会被挂起到互

斥锁****中,当锁释放的时候,也就被唤醒了。唤醒在后,解锁在前,当解锁后,就被唤醒了!

可以做一些处理,比如当队列中的商品量达到一半以上,就可以唤醒消费者来消费!

取商品

线程到来,先让其拿到锁,然后判断是否队列空了,空了就不能再消费,而是让其在_is_full条

件****变量下等待!

out输出型参数,让消费者拿到商品

然后唤醒生产者线程,以及解锁,也可以做一些处理,比如判断队列内商品是否少于一半,是的话

就可以唤醒生产者来生产!

前面传输数据只是第一步,还需要将数据进行处理,比如你买了零食,你还得去吃,而不是买回

看的!

这里以简单任务的形式来实现,比如+-*/%

定义一个命名空间,以及创建一个任务类,三个变量,两个操作数和一个操作符

然后对变量进行初始化!

**定义一个函数,来对这些数据进行算术运算! **

然后定义一个仿函数,调用时使用

测试

运算的数据以随机数的形式产生,创建两个线程,一个作为生产者,另一个作为消费者

生产者将任务派发给消费者

消费者在拿到任务后,进行执行

运行结果

也能多个线程共同执行任务

POSIX信号量

概念

信号量本质就是一个计数器,描述临界资源中资源数目的大小!(最多能有多少资源分配给线程)

多线程预定资源的手段:临界资源如果可以被划分为一个一个的小资源,如果处理得当,我们也有

可能让多个线程同时访问临界资源的不同区域,从而实现并发!类似于买电影票:预定资源

每个线程想访问临界资源,都得先申请信号量资源,只要申请到了,就一定会有你的小块资源的!

就如同你买电影票,只要你买到了,在那个时间段,那个座位就属于你了!

认识信号量对应的操作函数

如下图,是初始化信号量的函数接口,参数sem是定义的信号量变量,参数pshared是共享的进

程,****这里设为0即可,参数value是定义的信号量变量的初始值

如下图,是销毁信号量的函数接口

如下图,是申请信号量和释放信号量的过程

基于环形队列的生产消费模型

基本原理

生产者和消费者开始的时候,指向的就是同一个位置!队列为空的时候应该生产者先访问!

生产者和消费者在队列为满的时候,也指向同一个位置!应该消费者先访问

综上两点,就不能让生产和消费同时进行(互斥特性+同步特性)

那么,当队列不为空,也不为满的时候,生产者和消费者一定指向的不是同一个位置!所以生产和

消费可以并发执行!!!

基本实现思想

生产者,最关心的还是环形队列中空的位置!而****消费者,最关新的还是环形队列中的数据!这两者

都****是资源!!!

让其并发执行的3 条规则

生产者不能把消费者套一个圈,也就是生产者不能把之前存放的内容覆盖掉,否则就白做了!!!

消费者不能超过生产者

当指向同一个位置的时候,要根据空,满的状态,来判断让谁先执行

如下图,是一个简单的实现过程,一开始为空的时候,生产者,可能生产的巨快,就会不断的生

产,直到为满,就不让其生产了,当后来,为满的时候,消费者,可能消费的巨快,就会不断的消

费,直到为空,就不让其消费了!

代码实现

这里的环形队列以数组的方式来实现,所有声明了一个vector成员,_cap是这个环形队列的容

量,还有两个信号量成员_blank_sem和_data_sem,便于生产者和消费者申请和释放资源,

数组下标成员_c_step和_p_step,来便于生产者放商品和消费者拿商品!

如下图,是环形队列的构造函数和析构函数,来初始化成员变量和清理资源!

对生产者

如下图,生产者在乎的是队列中空的位置,所以先申请空位置资源!

如下图,是存放商品,每存放一个商品,就往前走一步,也就是_p_step++,当然,要将数组看上

去是一个环形的,就必须在存放完数组最后一个位置后,就走到第一个位置!所以需要取模运算

如下图,是释放数据资源!从而让消费者来拿数据

对消费者

如下图,消费者在乎的是队列中的数据,所以先申请数据资源!

如下图,out是一个输出型参数,所以先把要拿的数据赋值给*out, 然后也与生产者一样,往前走

如下图,是释放空位置资源!从而让生产者来存放数据

第一步已经完成,然后是处理数据!

与前面的阻塞队列一样,都以算术运算作为任务来做,所以直接将文件拷贝过来即可! 不过多加

了****一个函数,将待运算的表达式显示出来!

测试

创建两个线程,一个生产者,一个消费者,以及定义一个环形队列

生产者派发任务给消费者,消费者去执行

结果

多生产者多消费者

可以通过对生产者和消费者都加锁的情况来实现,即有2把锁,加锁申请信号量前面也可以,只是

就不符合多生产者多消费者的情况了,因为这种情况每一个线程都得申请到锁后,才能申请资源,

效率很低,就和单生产者和单消费者,没什么区别了!而如下图这样放,每个线程可能都能申请到

资源,虽然依然得排队等锁,但只要占用锁的线程释放锁,其它线程就能马上竞争锁,不用再去申

请资源,效率高了很多!!!此时的_p_step和_c_step也是临界资源了!

**运行结果 **

线程池

虽然创建线程的成本比创建进程要低很多,但是创建线程也是有成本的,而为了提高效率,所以就

有了线程池,比如你去奶茶店买奶茶,里面的员工都是提前培训好了的,你来给你把奶茶做好就行

了,而不是等你来买奶茶的时****候,才开始员工培训等等

概念

提前准备好的线程,用来随时处理任务,就称之为线程池!

如下图,线程池中的线程会竞争任务队列中的任务

代码实现

如下图,成员变量_num是在线程池中创建的线程的数量,_task_queue是任务队列,也是临界资

源!_mtx是锁,用来维护线程安全,_cond是一个条件变量,用来减少线程池中的线程做不必要

的****操作,比如当队列为空时,就可以将线程挂起,而不至于多次去判断队列是否为空等到有任务时

就****将其唤醒

如下图,是构造函数和析构函数,来对成员变量初始化,以及清理资源!

如下图,首先得创建线程,而传一个this指针给Rountine,则是为了访问成员变量,因为

Rountine必须得设置成为静态成员函数,否则就会隐含一个this指针,多了一个参数,不符合传参

的规定!从而编译报错

如下图,因为不想线程等待,所以首先实行线程分离,然后加锁,来维护线程安全,判断任务

队列是否为空,为空就将该线程挂起,再就是从任务队列中拿任务,解锁,以及处理任务,处理任

务放在解锁后面,是为了提高效率,可以多个线程同时处理数据,而不至于一个一个线程地去处理

如下图,在任务队列中放任务,放之前先加锁,来维护线程安全,解锁后,唤醒被挂起的线程来拿

任****务!

如下图,是拿任务,不用加锁,因为调用时会加锁!

测试

**如下图,还是和前面一样,用算术运算来处理数据 **

运行结果

单例模式

某些类, 只应该具有一个对象(实例), 就称之为单例

定义对象:1、开辟空间;2、给空间写入初始值,两步合在一起,就是对象初始化的过程,分开的

话,第二步,填入数据,就是赋值的过程,所以定义对象的本质:将对象加载入内存,而只让该对

象在内存中存在一份,加载一次,而什么时候加载或创建,就形成了两种模****式,一个饿汉模式,另

一****个则是懒汉模式

一般而言,我们的对象被设计成单例,有两个条件

语义上只需要一个

该对象内部存在大量的空间,保存了大量的数据,如果允许该对象存在多份,或者允许发生各种拷

贝,内存中会存在冗余数据

饿汉模式和懒汉模式(单例模式)

吃完饭, 立刻洗碗, 这种就是饿汉方式. 因为下一顿吃的时候可以立刻拿着碗就能吃饭

吃完饭, 先把碗放下, 然后下一顿饭用到这个碗了再洗碗, 就是懒汉方式

懒汉模式代码实现(以前面的线程池类来写

首先先定义一个静态成员指针变量ins,来确定整个类只有一个对象,同时将其初始化为nullptr

然后是将构造函数私有,以及拷贝构造和赋值重载给删掉!来防止在栈上创建对象,以及通过其它

对象来创建对象

再就是定义一个静态成员函数来创建对象,因为只有静态的,才能属于整个类,可以以类名调用

首先对里面的if判断,当有对象了之后,就不能创建对象了,而是直接返回,所以加了个if判断语

句,其次因为有多个线程,是存在线程安全的,所以需要加锁!而在外面再加一层if语句判断,是

为了减少锁的征用,比如当已经创建好对象之后,其它线程还要来创建,每次都要申请锁,释放

锁,就会降低效率,所以再加一层if语句,是为了提高效率!!!

测试

运行结果

读者——写者模型

"3 2 1"原则

三种关系:写者和写者,写者和读者,读者和读者

写者和写者:互斥关系,比如出黑板报时,两个人出,假设不能划分区域,就只能轮流去写

写者和读者:互斥关系和同步关系,比如当某个人正在出黑板报时,你正在读,你才看一半,他发

现不对,又做修改,你就很难受!所以两者是互斥关系,而当出的黑板报,过了几天都没人来读,

虽然合理,但是没有意义!所以就需要出完就有人来读,当没有人来读了,就应该重新出,所以两

者也有同步关系

读者和读者:没有任何关系,因为两个人来读,可以同时读!

两种角色:读者和写者

一个交易场所:一段缓冲区(自己申请的,或是stl)

生产者——消费者模型与读者写者模型的区别

根本原因:读者不会取走资源,而消费者会拿走数据!

使用代码完成读者写者模型,本质:使用锁,维护上面的三种关系!!!

读写锁的系统调用接口

初始化锁变量和销毁接口

以读者身份加锁

以写方式加锁

无论是以上面哪种方式加锁,都用下图的解锁接口

如何理解,伪代码实现

能采用的条件

对数据,大部分的操作是读取,少量的操作是写入

判断依据是,进行数据读取(消费)的一端,是否会将数据取走,如果不取走,就可以考虑读者——

写者模型

优先级

读者优先:读者和写者同时到来的时候,我们让读者先进入访问

写者优先:当读者和写者同时到来的时候,比当前写者晚来的所有的读者,都不要进入临界区访问

了,等临界区中没有读者的时候,让写者先写入

默认:读者优先,因为读者多,写者少,所以是存在饥饿问题的(中性词)

挂起等待特性的锁与自旋锁

我们可以根据线程访问临界资源花费的时间长短,来决定使用哪个锁,因为将线程挂起等待是有成

本的!如****果花费的时间非常短,就比较适合自选锁,来不断的通过循环,检测锁的状态,而如果花

费的时间****非常长,则比较适合挂起等待锁

线程会在临界资源中待多长时间,线程不知道,但程序员知道!!!

接口

标签: linux 运维 服务器

本文转载自: https://blog.csdn.net/weixin_58867976/article/details/126939102
版权归原作者 风影66666 所有, 如有侵权,请联系我们删除。

“Linux多线程”的评论:

还没有评论