线程概念
线程的优缺点
线程控制
线程互斥
线程同步
死锁
生产者——消费者模型
线程池
单例模式
读者——写者模型
挂起等待特性的锁与自旋锁
线程概念
线程(一般教材):是再进程内部运行的一个执行分支(执行流),属于进程的一部分,粒度要比进程
更加细和轻量化
常规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)
生产者——消费者模型与读者写者模型的区别
根本原因:读者不会取走资源,而消费者会拿走数据!
使用代码完成读者写者模型,本质:使用锁,维护上面的三种关系!!!
读写锁的系统调用接口
初始化锁变量和销毁接口
以读者身份加锁
以写方式加锁
无论是以上面哪种方式加锁,都用下图的解锁接口
如何理解,伪代码实现
能采用的条件
对数据,大部分的操作是读取,少量的操作是写入
判断依据是,进行数据读取(消费)的一端,是否会将数据取走,如果不取走,就可以考虑读者——
写者模型
优先级
读者优先:读者和写者同时到来的时候,我们让读者先进入访问
写者优先:当读者和写者同时到来的时候,比当前写者晚来的所有的读者,都不要进入临界区访问
了,等临界区中没有读者的时候,让写者先写入
默认:读者优先,因为读者多,写者少,所以是存在饥饿问题的(中性词)
挂起等待特性的锁与自旋锁
我们可以根据线程访问临界资源花费的时间长短,来决定使用哪个锁,因为将线程挂起等待是有成
本的!如****果花费的时间非常短,就比较适合自选锁,来不断的通过循环,检测锁的状态,而如果花
费的时间****非常长,则比较适合挂起等待锁
线程会在临界资源中待多长时间,线程不知道,但程序员知道!!!
接口
版权归原作者 风影66666 所有, 如有侵权,请联系我们删除。