1-17.同步与互斥

1.正确看待生产消费者模型的高效问题

在这里插入图片描述

生产者对应的数据应该从哪来呀?
从 用户 或 网络
对于生产者不仅仅把生产数据放到对应的仓库里叫做生产,要意识到生产者对应的数据来源也是要花时间来获取的。
消费线程在消费的时候,是不是要对数据做加工做处理,不要只认为消费者把数据从仓库里拿到自己的线程中就叫消费了,还不够只是一半,另一半是消费者拿到数据后要不要做数据加工处理?也要花时间!
所以生产者要做的工作:
1.获取数据
2.生产数据到队列
消费者要做的工作:
1.消费数据(把数据从队列里拿走)
2.加工处理数据
不能只看到生产数据到队列 和 消费数据,同样也要看到 获取数据 和 加工处理数据的过程

干什么突然说这个,这有什么了不起的?
生产消费者模型有很多优点,支持忙闲不均,让生产和消费进行解耦拉
可是也有人说生产消费者模型是高效的
问题是
可是生产消费者模型为什么高效?
有人说别扯什么高效,因为生产的过程是要加锁的,消费的过程也是要加锁的
而且还是同一把锁
在这里插入图片描述
所以整个访问仓库的过程中,生产和消费本身是串行的,也就是生产一个对方才能消费,你扯什么高效?
所以白菜心里面没有高效
那么在哪里提现高效了呢?
如果消费者正在进行仓库消费,生产者虽然没办法直接生成,但他可不可以正在获取数据
如果生产者正在持有锁向仓库中放数据,消费者呢不消费,有没有可能消费者正在进行加工处理数据
所以如果在CP模型中引入生产者获取数据 和 消费者也要加工处理数据,这也要花时间
就会存在非常大的概率生产者可能正在生产,但消费者正在进行加工处理,
那么生产和消费线程不就是并发访问的吗?
说白了一个正在访问临界区代码,另一个正在访问非临界区代码,这样两线程不就高效并发的运行起来了吗。
所以CP模型所谓的高校性体现的不是生产者和消费者两个互磕访问临界区而是有一定概率生产者和消费者的临界区
和非临界区会出现并发的,并发情况下生产和消费的动作就是同时跑的,所以CP才是高校的。
所以CP模型不能只看到白菜心中间一部分,再怎么看生产和消费就是互斥同步的,这部分谈不上高校,放任务和拿任务
根本就不高效,因为你加锁了
还有一种可能是仓库还有任务,生产者也不生产,消费者也不消费,生产正在构建数据,消费者正在加工处理数据,并不访问对应仓库
他们两个非临界区之间就并发式的在进行访问了,两个没有任何耦合,就完成了高效的闭环了。
拿生产者和消费者的临界区对比那他们俩就不高效,只有生产者的非临界区代码执行时和消费者的非临界区代码执行时,或者消费者的
临界区代码执行时此时他们交叉时彼此间才不会互相干扰。
尤其是多生产多消费时,他们多线程并发访问时只有少量执行流进行临界区互斥同步,大量线程都在并发访问


再谈一下 pthread_cond_wait 意义

在这里插入图片描述

拿消费者pop举例
不管是抢票还是CP模型,他们的判断临界资源是否就绪都是在加锁解锁之间的,为什么要有这个判断呢?不是说你抢到了锁就能生产或消费,抢到了锁就能抢票,抢票也要看票还有无
判断临界资源是否满足 是在访问临界资源! 所以要在加锁解锁之间
如果临界资源不就绪,那你就要去等待,等待资源就绪就行了,可是等是在判断内部条件成立后,它在加锁和解锁之间,你线程是持有锁的!!你这个线程敢直接去等吗?如果线程直接去等,结果就是抱着锁去等待了,没有人释放锁,其他线程根本就进入不了临界区比如说生产者就进不来生产不了,那消费者也就不可能被唤醒,那消费者等也白等
所以调用pthread_cond_wait时去特定条件变量下等时,它会自动把锁释放掉,同时因为唤醒而返回 的时候,要重新持有锁继续向后执行。然后这个函数才会继续向后走
这句话回答了上一篇中的问题

2.伪唤醒

如果线程wait时,被误唤醒了呢??我们之前讲的CP模型是单生产单消费,我们一会可以改成多生产多消费者
什么叫伪唤醒啊?
我们以生产者为例,如果此时阻塞队列是满的,那么所有的生产者就要去条件变量的队列中去等待,
在这里插入图片描述

在这里插入图片描述
此时消费者就来消费了,它消费了一个任务,假设只有一个任务被拿走了,此时消费者要唤醒生产者来进行消费,如果消费者不是调用
pthread_cond_signal 而是 pthread_cond_broadcast 唤醒了所有的生产者,或者是有多个消费者可能向条件变量下发起了多次唤醒不管是 broadcast 还是多次唤醒(多线程中消费者有可能发了很多次signal 或者说 signal和生产者的wait 并不是一一对应),反正三个线程都被唤醒了。
在这里插入图片描述
此时所有生产者就要重新持有锁,就要去锁的队列中等待 或者 说对锁资源进行竞争,假设其中一个生产者抢到了 锁,正常生产了到了阻塞队列中最后一个位置,在继续唤醒消费者来消费 这个生产者生产完了最后一个位置它自己一解锁,此时 唤醒的消费者 和 先前已经被唤醒了的 生产者,一起竞争锁,如果消费者没有抢到这个锁而是生产者抢到了这个锁,生产者已经不再条件变量下等待了哦,生产者抢到了锁就继续往队列里生产,那么就会导致已经满了的队列 溢出错误。

在这里插入图片描述
这种情况,一个线程在他被唤醒时,他是因为条件满足被唤醒的,可是它被唤醒时历史上的条件满足已经被其他线程处理掉了,当你再去生产时,已经没有空间供你生产了,这时该线程就被称为伪唤醒状态
这个伪唤醒的线程唤醒以后也是直接执行q.push
在这里插入图片描述

从另一个角度去理解
pthread_cond_wait 是一个函数,如果这个 函数调用失败了(虽然失败几率很小),那么等待也就是失败了,函数返回直接向后执行
如果此时队列已经满了,那么同样会出错

解决伪唤醒:循环判断
历史上可能有很多个消费者发出的signal唤醒,或者 broadcast
多线程唤醒就生产者就不再条件变量下等待了,它的有序性就没了。
所以 条件判断时 要用while 而不是 if 做到防止线程被伪唤醒, 让它重新再判断条件是否满足,如果不满足就重新
投入到条件变量下等待
在这里插入图片描述
多生产,单消费,消费者一下唤醒一大批生产者,生产者和消费者竞争锁竞争到了,因为if 不会重新再去判断条件是否满足 导致出现问题,生产者和消费者一起竞争导致有序性失效

单生产单消费 改 多生产多消费

在这里插入图片描述
多生产多消费也是用的是同一个阻塞队列
在这里插入图片描述
为什么这样改直接就能改成多生产多消费呢?
因为 321 原则
3种关系 cc pp cp
2种角色 – 生产和消费
1个交易场所 — 特定结构的内存空间

我们现在依然可以维持住 多个生产者之间的互斥关系 多个消费者之间的互斥关系
多个生产者和多个消费者之间互斥关系,因为我们用的是同一把锁,同步关系利用两个条件变量也可以控制让生产和消费具有一定顺序性,消费者抢到了锁如果队列里没数据就只能让生产者先来生产
在这里插入图片描述
即便此时是多生产多消费,任何一个时刻只允许几个人访问临界资源呢?
只允许 1 个,本来 阻塞队列就是 临界资源,就只能让一个人来生产消费了
所以加锁一个锁就够了,所以一瞬间就改成多生产多消费了,因为它满足321原则

那我为什么闲的没事干改成多生产多消费呢?最后不是只有一个人在仓库里生产 一个线程去仓库消费吗
,看来没必要搞成多生产多消费啊?好像没意义啊?如果有意义 , 意义在哪里?
最大的意义生产消费不是只考虑生产放到队列 或者 从队列里拿出去 ,生产一下拿一下有什么意思呢?
获取数据和处理加工数据都要花时间
多生产 多消费最大的并发意义不在于从阻塞队列里拿放的时候怎么样怎么样,如果多生产者往队列里放的时候不是要和消费者竞争吗(满了不生产,空了就不消费这种策略),一个生产者申请锁时,其他生产者就可以并发获取数据。
每一个消费者拿了数据以后处理数据,花时间处理时,也可以有的消费者去竞争锁
有一个线程竞争锁就行了,剩下有点已经获取数据和处理数据花时间时肯定不需要申请锁,其他没有申请到锁的没有拿到任务的它去申请锁啊,所以并发度就上来了。
在这里插入图片描述

3.信号量

理论

互斥和今天的信号量是一码事 ,这个信号量是线程用的 和 进程的SYSTEMV不一样

今天我们阻塞队列是当做一个整体使用的,,加锁只有一个线程能进来,但是共享资源也可以被看做多份
在这里插入图片描述
问题
如果一个数组,分成三段,有三个线程分别独立访问每个部分,虽然数组是一个全局资源,但是多线程访问的是全局资源不同的区域,所以多线程可以并发访问对应数组的不同区域吗?
如果来第四个线程呢?那你就比较头大了。我们既要保证并发度又要保证线程安全,
所以四个线程只能放三个进来访问数组不同区域,放哪三个进来呢?哪三个不重要只要是三个就行。
所以数组资源当做整体就直接加锁只有一个线程能进来,如果资源允许被局部性的可以被多线程并发访问不同区域,你能被拆成
多少分资源,你就最多允许多少个线程进入。
所以为了更好的保护临界资源,你说放三个就三个进来,凭什么保证呢?
所以就需要信号量的概念被引入进来了。

信号量 本质是一把计数器,描述临界资源中资源数量的多少
数组有三百个元素被分成三份了,描述数组临界资源资源数目就是3,每个线程用100个元素。

之前电影院卖票例子中,
1.只要我们限定进来的线程个数不要超过总的座位数
2.合理的把资源分配给不同的线程,不要出现把一个资源分配给两个线程的情况
此时就可以保证多线程并发访问电影院资源
问题
在电影院看电影时,我把票买到了这个资源就属于我了,还是我真正做到座位上资源才是我的?
答:买了票就是,我连电影院不去这个位子要给我留着
所以买票的本质是对电影院座位资源的预定机制
只要买到了票你一定有座位,没买到票对不起你一定没座位
所以我们每次想看电影前,我们都先去买票
同理
我们将来要访问一个临界资源,资源有很多,我们可以定义一把信号量,所有的线程想去
访问临界资源中的某一个,先别急访问,全部每个线程都先去竞争去抢占信号量,申请到信号量
的线程你就一定有一个资源将来给你,要是没申请到,对不起你不能进入到临界资源去访问
所以这个就叫信号量。

我们之前谈了,申请信号量,信号量计数器就要减减,表明资源已经减少
当你归回资源时,信号量计数器就要加加,资源又多了一个。

我们多线程访问竞争信号量,对信号量计数器做加加减减 必须保证信号量本身也是原子的,
在这里插入图片描述
在申请信号量时,做减减和加加操作必须是原子的。
对一把计数器保证原子性我们已经能做到了,一个整型变量给他加锁解锁就行了。
所以对信号量来讲,像这种原子性的去修改一个被大家可以看到的计数器,这个东西就叫做信号量

所以信号量是一个保证PV操作的原子性的一把计数器
在这里插入图片描述
这里有一个点是没有说清楚的,你说过申请信号量成功了我们就能保证线程一定能得到
某一个资源,具体是哪一个呢,你怎么保证他和别人不冲突呢?
这个就由程序员编码实现的,信号量保证不会有多余的人进来,但你要保证让进来的人坐在自己的位置上。

信号量的本质是一把计数器,它的操作就是PV操作 ,那么这把计数器的本质是什么?
在这里插入图片描述

答: 用来描述资源数目的。

所以请问一旦我们用P操作成功后,我们在PV之间(临界区中 和 之前锁一样)还用判断资源是否是就绪 或者 满足条件 吗?
答:不需要,因为只要申请成功了,就一定有你的,申请不成功的你就去信号量去等待了,所以PV操作,P只要成功,只要进了门告诉往哪里走就行了,不要告诉我资源有没有就绪,不需要判断了
计数器把资源是否就绪放在了临界区之外 ,申请信号量时,其实就间接的已经在做判断了!

那加锁为什么就要判断?
我们目前为止是把阻塞队列q当成一份资源使用的,没有拆它,这种场景你也拆不了
在这里插入图片描述
我们每一个人申请队列资源时,都要先判断资源是否存在(加锁 pthread_mutex_lock), 只要你持有了这把锁就意味着
队列资源可以被你访问,加锁也可以看做一个二元信号量(0,1),只有一份资源的共享资源。那有人就说了作为整体了按照信号量你申请成功了就一定能访问这个资源了,我访问了啊,这资源直接就让我访问了,我这判断不就是在访问队列资源吗
在这里插入图片描述
只不过整体把它申请了,但你使用的时候他有使用条件(push时,队列满没满,满了就别放了),所以你就只能在内部做判断了

如果把资源当做整体来用,把信号量计数器设置为1,此时信号量其实就是一把互斥锁

使用

在这里插入图片描述
在这里插入图片描述

生成消费模型之环形队列的CP模型

在这里插入图片描述

环形队列用数组模拟,队列有头有尾,我们利用数组下标取余的方式可以让下标绕回0下标,这个数组就相当于环形队列了

环形队列的判空和判满 问题 , 其实我们有信号量这个神奇不是问题,理解信号量的意义比理解环形队列本身意义要大得多。

正常不加任何约束的情况下,一开始头尾指针指向同一个位置,按照tail一直往队列里放,放到最后满了tail和head会相遇,
则空和满的时候,tail和head指向的是同一个位置,无法判断是空还是满

在这里插入图片描述
解决 空还是满指向同一个位置

1.计数器
默认计数器是0,生产就++,消费就减减,最后判空判满就判断计数器是不是0还是最大容量

2.空一个位置,通过浪费一个格子的方式来判空判满

head tail指向同一个位置

tail的下一个位置是不是head,tail+1 % 队列总长度 tail+1肯定要做模运算不然就越界了
在这里插入图片描述
上边是数据结构中环形队列的问题,我们今天有信号量用不着这么麻烦。

我们还是先单生存单消费
在这里插入图片描述

问题
按照队列尾进头出的规则,在生产者生产的同时,只要生产者和消费者不在访问同一个数组位置,能不能在生产的同时进行消费。可以

那生产和消费什么时候会指向同一个位置?
先聊一个追逐游戏
在这里插入图片描述

一个人转着圈放苹果,另一个人追着我拿苹果
必须要满足三个条件:
1.在同一个位置时,只能一个人访问
空:只能我访问,生产者
满:只能你访问,消费者

⒉你不能拿苹果超过我

3.我不能放苹果把你套一个圈
开始时我和你在一个位置,我开始放,你在后面拿这个过程中
输出一个结论:
我们两个什么情况才会指向同一个位置? 空或者满
这句话的反面就是,
不空和不满的时候,我们一定指向不同的位置,我们可以同时访问!

CP模型中如何利用代码来实现这三个条件呢?
信号量是一把计数器,描述资源数目的
我们阻塞队列中生产者和消费者关心的好像都是数据,其实他们关注的其实不一样,生产者关心的是还有没有位置放,消费者关心的是还有没有数据可以拿。
接下来你要用信号量表示资源的话,你得首先明确生产者和消费者各自关注什么资源呢?

在这里插入图片描述
P关注什么资源呢?P其实不关心队列里有多少数据,它最关心环形队列里还有多少剩余空间,我要根据空间数来放我的数据。
C关注什么资源呢? C最关心环形队列里还有多少剩余数据。
好比我放苹果从来不关心已经放了多少苹果,我只关心还有没有地方放,我拿苹果的只关心还有多少苹果没拿,不关心空盘子。

在这里插入图片描述
所以定义两个信号量 ,一个表示空间还有多少,一个表示数据资源有多少。
信号量初始值,空间资源spacesem是N (环形队列长度),最开始环形队列里数据为空,那datasem = 0

对于生产者来讲,它要生产数据,他得先申请一个空格子,只要有空格子他就一定能生产成功
,所以它得先P(Spacesem)操作 申请Spacesem格子资源
然后在进行生产

对于消费者来讲,他也要消费,他得先保证有数据,用P(datasem)申请数据资源,然后再消费

问题在于
请问生产者和消费者两个线程最开始队列为空时,两个线程一定是谁先执行?
一定是生产者先执行,因为消费者申请数据资源申请不到,数据资源是0,但是格子资源是满的,
这就能保证,为空时,两个人指向同一个位置 保证生产者先运行。
当队列满了时,空间是0,生产者申请不到资源无法生产,但是数据资源是N,则一定是消费者先消费。
生产者生产完了一个数据,此时格子被占了,没有归还空间资源,但是数据资源多了一个,所以
V(datasem)。
数据资源变多了,生产者此时已经指向下一个位置了,生产者和消费者不再指向同一个位置了,消费者这时可以来消费了,
当消费者消费走了这个数据后,数据资源少了一个,但数据被拿走,空间资源就多了一个,就要V(spcaesem)。

假设最开始为空时,一定是生产者先跑,消费者就不跑,生产者就一直生产最后队列满了,生产者就不可能再生产了。
因为生产者都要P自己格子资源,格子越用越少,当生产满了格子就没了,没了P的时候就会被挂起。
这就叫生产者无法把消费者套一个圈,因为生产者生产满了时,格子已经被消耗完了,空间信号量被减到了0,此时就无法再生产了,无法把消费者套一个圈。
在这里插入图片描述

那消费者就来玩了,他就一直消费数据资源,空间资源在增多。
空满都会指向同一个位置,消费完了能不能超过生产者再往后消费呢?
不能,因为你的数据最多放n个,你消费完了就不能再往后消费了。
datasem = 0 了,这叫做消费者无法超过生产者
在这里插入图片描述
所以队列空了,生产和消费都来了,为空的情况下,消费者跑不动,因为数据资源是0,但是生产者的空间资源又是N,又继续让生产者先运行。

所以他们申请自己的资源,释放对方的资源,就可以做到满足追逐游戏的三个条件。
也就是为空或为满时,生产消费指向同一个位置,但一定能保证其中有一个先运行,除了这两种情况之外,我们两个都可以并发去访问,这就叫做基于环形队列的生产者消费者模型。

代码实现
单生产单消费使用同一个环形队列
在这里插入图片描述
把rq传给两个线程函数,他们就会访问同一个队列了。

设计环形队列
定义一个vector充当环形队列,你如果定义数组,那数组是不是空等就要自己维护起来,麻烦
定义一个容量cap方便操作

生产线程和消费线程他们两个是有不同的生产和消费的位置 或 下标的,因为他们两个90%生产消费
都不是同一个位置,只有空满才会指向同一个位置,另外我们只要申请信号量成功就能保证生产或消费拿到他想要的资源,至于你想把那个资源给生产,哪个资源给消费,你得自己定好,这就是程序员得自己维护线程要的资源你打算给哪一份。
所以定义 消费者下标 和 生产者下标
在这里插入图片描述
剩下的就是定义 生产者关注的空间资源 信号量 以及 消费者关注的数据资源
该初始化初始化,该销毁销毁
在这里插入图片描述
问题
信号量能保证生产者和生产者之间的互斥吗?
不能
目前是单生产,单消费,后面要改成多生产多消费 ,信号量无法保证多生产者之间 或 多消费者之间的互斥。
因为格子资源开辟一大堆,多生产你能申请到,我也能申请到,多生产多消费中,你的生产者 或 消费者的下标只有一个!但是资源有多份,所以就不能让多生产 或 多消费 同时进来,因为他们会对下标展开竞争。
他这个不同区域只是生产和消费是不同区域,多生产多消费目前做不到。

然后就像我们讲的,用信号量对资源进行预订,生产者放一个位置加加后需要%,维持环形特性
在这里插入图片描述
先把p_step_++放在PV操作外面,目前单生产,p_step属于生产者线程私有
再看消费者
在这里插入图片描述
未来执行生产 或 消费时,这两个线程执行时他们是无序的,但是为空为满时虽然他们的下标是一样的,先别管到底谁先进来,但是只能有一个线程能够进入环形队列生产或消费,只能有一个这不就是在为空为满时表现出局部性的互斥特征。因为信号量的资源数量控制。
第二个,为空的时候,整个代码会维护让生产者先运行,为满的时候,代码自动维护让消费者先运行,
这不就是指向同一个位置时,在不同的情况下让生产和消费具有一定顺序性,局部性的同步也就出来了。

如果不为空并且不为满呢,生产和消费的两个下标的值一定是不一样的,生产者玩生产的,消费玩消费的,各玩各的,他们两个其实并没有表现出非常明显的互斥特征,此时他们俩个不就是在并发运行吗。这就和我们的阻塞队列不太一样了,阻塞队列是把队列当做一个整体使用,我生产者加锁的时候你消费者就不能访问,今天环形队列不为空不为满时消费者和生产者可以并发运行。

后面就是考虑生产者要花时间获取数据,消费者要花时间加工数据的问题,我们是用随机数模拟
在这里插入图片描述
在这里插入图片描述
可以让生产者或消费者之中 的 某一个慢一点,会以慢的那个节奏为主。

问题
如果今天是多生产 和 多消费,那么该如何修改代码呢?
根据321原则,生产者和生产者之间也要维持互斥关系,消费者和消费者之间也要互斥。
生产者的下标只有一个,假设不维护生产者之间的互斥关系,三个生产者线程同时进入Push,可能3个生产者都P(pspace_sem)都申请信号量成功,这是有可能的。虽然P操作是原子的,我们一个个来,线程123各自申请信号量,肯定能成功,可是多线程同时进入资源中,大家对于生产下标不就展开竞争吗,我想放他说他也想放,我们不就把数据覆盖掉了。
321原则是有道理的,主要原因是生产者和生产者有资源(生产者的下标)是共享的,消费者同理
在这里插入图片描述
即便我把它改成了多生产和多消费,也不是我想的那样多生产多消费同时跑环形队列里去搞事情,不是的。
即便是多生产多消费,在任何时刻,只能有一个生产者或一个消费者在环形队列里从事生产或消费,还有一种是只有一个生产者 和 一个消费者 在同时跑,除此之外不能同时允许 两个生产者 两个消费者 同时在环形队列里操作。

目前生产者和消费者的同步和互斥关系已经由信号量承担了,接下来要维持生产者之间的互斥和消费者之间的互斥。
肯定要选择加锁了,此时选择加几把锁呢?
两把,因为环形队列只要不为空不为满生产和消费可以同时跑,生产者和消费者各自维护自己之间的互斥关系就可以了,生产和消费之间的互斥关系由信号量来维护。

以生产者为例,加锁要保护下标以及下标访问,还要保护下标的加加移动,多线程申请信号量这件事情其实
是不冲突的,会出现冲突的就是这三行
在这里插入图片描述
如果你这样加锁
在这里插入图片描述
加锁应该放在申请信号量之前加锁 还是 申请信号量之后加锁? 1 还是 2 ?
在这里插入图片描述
我们选择2 ,那2为什么行?
首先技术角度
信号量资源是不需要被保护的,因为他的申请是原子的,你给我加锁干什么,我不怕。
而临界区中的代码一定要相对比较少,不需要把信号量放在加锁之后。
站在逻辑角度,
如果先申请锁,成功的人才去申请信号量,所以申请锁花的时间和申请信号量的时间是串行的,
而2的话,多线程进入时能做到一定程度申请锁和申请信号量的时间是并行。
在一个线程生产的同时,其他线程可以获取信号量。
但是1 就不行了,必须加锁才能 申请信号量,那申请信号量的线程永远只有一个,不能很好地提高并发度。

上一个线程一旦生产完了释放锁了,大概率下一次也不能立马再申请锁了,因为它先要申请信号量,而其他线程已经申请到信号量的就高优先级申请到锁。

所以完整的加锁解锁应该是这样
在这里插入图片描述
在这里插入图片描述

在未来多生产多消费时,宏观上永远只有一个生产者或者一个消费者进到了环形队列里。现在我加了锁,所以即便是多生产多消费,其实本质上真正玩的进到环形队列里永远只有一个生产者 ,一个消费者。
暂时不考虑为空为满的情况,实际上代码中任何时刻只有一个生产者,任何时刻只有一个消费者,生产者和消费者因为用的不同的锁,他们两个可能会在不满不空的情况下同时访问。

所以其实为空为满同一位置时,就由信号量来保证生产和消费之间的互斥和同步的特定。
不为空不为满,生产和消费两个其实压根不会互相干扰,用着不同的锁,用着不同的信号量,访问着不同的下标,所以各玩各的,我们双方只需维护好自身的互斥关系就可以了。

所以直接在main里改多生产 多消费就可以了。多生产多消费看到的是同一个环形队列。
在这里插入图片描述
把每个线程名字也带上,打印结果可能会根据生产者还是消费者互相竞争的去跑,看的不太明显


//可重入 与线程安全
12-06
2:45
描述侧重点
线程安全 不安全 描述的是多线程并发的问题
重入 不可重入 描述的是函数的特点

重入 不可重入
只是描述函数的特征而不是褒贬,不可重入那就需要程序员加锁
函数不可重入,多线程调用时可能会出现线程安全问题
如果函数可被重入的,多线程调用时绝对也是线程安全的
可重入的概念就是允许多线程直接访问


线程池
池化技术―以空间换时间
C++类内创建线程原生线程

封装线程类

----------------

单例模式设计线程池涉及的加锁双if问题 饿汉 懒汉

自旋锁  
在这里插入图片描述
库中自带自旋锁接口
如果临界资源访问时长不长就建议 ,加锁失败后不线程挂起阻塞,而是直接继续尝试加锁

读者写者模型
默认读者优先,读者多 写者少
除了读读关系,其他关系都和CP模型一样
读读是可以共享的,所有读者都可以同时读取而不拿走数据
在这里插入图片描述
为什么生产消费,消费者是互斥?
因为消费者是真的把数据拿走

理解一下黄色的两个接口
在这里插入图片描述

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值