2019/12/23 02-RLock和Condition

在这里插入图片描述
锁是非常重要的技术,并发的时候,都在争抢同一个资源的时候,这个时候就需要考虑是否使用锁,锁有好处和坏处
,有了锁,并发的运行效率降低,耗时降低,但没有锁就得不到你想要的结果

用锁也是有一些原则的,尽量少的使用锁,在必须使用的地方再使用,
尽量短的使用锁,锁里面的语句能少则少(比如高速公路过关卡,收费站人员故意拖慢时间,后面等的时间就增加了)

所以争抢一个资源,就需要尽量快的去使用,然后把锁释放掉,一定避免死锁,死锁出现了就非常头疼,死锁 出现了,如果不kill进程,基本就没有好办法了,应该尽一切可能避免死锁

锁的使用很简单,两个方法acquire,release

在这里插入图片描述
这三处去释放其实都不优雅
在这里插入图片描述
应该使用try fianlly,还有一种是with语法
在这里插入图片描述
拿到锁一定要释放,否则就是死锁
在这里插入图片描述
用了锁,效率就低下,这是一定的,碰到锁,原来并行的就变成串行的了,大家都访问一个资源的时候,加锁的地方,就是一个瓶颈,使用锁了,就需要尽快释放掉,
在这里插入图片描述
阻塞和非阻塞的时候要小心使用,阻塞的时候,考虑很简单,下一句不能执行,非阻塞的就会有一些问题,
下面代码就是,开启10个任务对象,每一个任务对象,除了按名字,还需要加个lock,每个任务对象有自己的锁

在这里插入图片描述
下面开启5个线程,会遍历当前的线程列表,等于有10个任务交给5个线程去跑,5个线程里面,遍历任务列表,如果在去任务对象 的锁上去请求,如果请求到了,则打
在这里插入图片描述
如果请求到了,则打印在这里插入图片描述
请求失败,打印这个
在这里插入图片描述
第一个线程起来,遍历任务列表的时候,先遍历任务列表的第一个任务,尝试对第一个任务获取锁,获取成功后可以在里面执行,一直在处理没释放
在这里插入图片描述
如果现在第二个线程启动了,遍历的时候先访问第一个任务,只要第一个线程在第一个任务上没有释放,所有后面的线程都阻塞了,就没有什么可以执行的了在这里插入图片描述
如果采用非阻塞行为,第一个线程在第一个任务上,在访问的时候,获得了一个任务的锁,第二个再去这个任务的锁上获取锁,获取失败,这样就可以去拿第二个任务,现在没有人管第二个任务,就获得第二个任务的锁
这时候第三个线程启动了,启动之后,依然去遍历任务列表,第一个,第二个任务都获得锁失败,就会尝试获得第三个任务的锁,结果获取到了,如果把非阻塞效果,false改为true,这样就只能跑一个线程,因为其他线程获取不到锁,就全部阻塞了

在这里插入图片描述
想清楚,true和false的差异在这里插入图片描述
非阻塞的时候,有可能acquire返回true和false,如果是永久阻塞得到的结果一定是true,否则永远都不改,所以写代码只要考虑下面的即可
在这里插入图片描述
非阻塞就需要考虑,拿到了怎么样,没拿到怎么样,大多数情况写阻塞,简单快速

可重入锁,也称为递归锁在这里插入图片描述在这里插入图片描述在这里插入图片描述
有owner和count,现在是未上锁的
在这里插入图片描述
在这之前没人拿到锁
在这里插入图片描述
数字加1了
在这里插入图片描述
lock。acquire永久阻塞的方式,依然能拿到
在这里插入图片描述
现在拿了两次锁没有释放掉在这里插入图片描述
说明释放的时候,count是减少的在这里插入图片描述
两个release就没有了在这里插入图片描述
没有获取的锁,release就报错,也就是不可以多次release在这里插入图片描述
锁三次,release两次,就剩一个,默认acquire是阻塞的在这里插入图片描述
当前环境下,可以连续获得锁在这里插入图片描述
拿到几次锁就需要释放几次锁,总共锁了4次,释放了两次,是2,没有问题
在这里插入图片描述
在这里插入图片描述
为什么拿不到锁,就是因为跨线程

现在这个锁到线程调用前都没有acquire在这里插入图片描述
count=0

在这里插入图片描述
现在打印出来是0
在这里插入图片描述
修改一下代码,再次打印,再另一个线程可以获得锁在这里插入图片描述
现在变成这样
在这里插入图片描述在这里插入图片描述
拿三次,释放三次,看看里面的能否打印出来
在这里插入图片描述
如果在另一个线程要用同一个锁对象的时候,只要发现这个锁对象的count=0,才有资格申请,如果count不等于0,就申请不到

现在这个owner是线程id
在这里插入图片描述
查看rlock代码
在这里插入图片描述
这就是取线程ID,把线程ID变成自己的owner,把ID给owner,然后count+1
在这里插入图片描述
如果线程占用了,count一定会+1,也就是count=0的时候,才允许切换,另一个线程才能获得这把锁,线程获得锁之后,owner就会换成当前线程ID,因为getident是拿的当前线程ID
在这里插入图片描述
Rlock是与线程相关的锁,实验发现,Rlock只要调用rcquire,首先看count是什么,如果count是0,就允许当前线程ID替换成当前owner,替换了一次,count+1,之后,如果有别的线程过来访问这把锁,count!=0,不允许访问,访问失败,阻塞还是不阻塞跟你后面写的acquire参数有关,当前线程把获得的所有次数,全部release完,count清零,其他线程才能获得这把锁,其他如果某一个线程获得了这把锁,立即将owner改成自己的线程ID,在哪个线程获得这个锁的线程内,可以连续多次acquire,也就是可以连续多次获得锁,
其他线程包括主线程,只要是某一个工作线程获得这把锁后,包括主线程,都不能碰这把锁,因为这把锁owner不是你。
rlock是跟线程相关的,(现在学的跟线程相关的锁,thread.lock,RLock)

在同一个线程里,可以多次获取这把锁,但是使用锁的基本原则没有改变,用了多少次锁,就要release多少次,否则不会count=0,count!=0,其他线程根本没有资格获取这把锁,所以相当于又造成死锁现象,
在这里插入图片描述
主线程就卡住了,总之,这把锁被某个线程占了以后,只要不清0,其他线程访问,全部都获得不到,如果用的阻塞方式,就阻塞住了
在这里插入图片描述
可重入锁,也称为递归锁,是线程相关的锁,由owner来决定,当前是哪个线程获得这把锁,线程A获得锁之后,就可以在自己线程 内多次获得这个锁,都不会阻塞,要求每个线程acquire多少次,就需要release多少次,在这里插入图片描述在这里插入图片描述
延迟三秒执行,后面的函数,可以lambda在这里插入图片描述在这里插入图片描述blocking=false不阻塞
在这里插入图片描述
跨线程要报错,报错在于,锁根本就不是你的
在这里插入图片描述在这里插入图片描述
可重入锁和线程相关,可以在一个线程中获得这个锁,一旦获得锁,属主就归你了,可以在线程中多次获得这个锁,当锁未释放完,其他线程获取锁就会阻塞,也可以(按照你指定的不阻塞也可以),阻塞必须到持有这把锁的线程释放掉,release完,count为0了,其他就可以抢这把锁 了。
所有的锁应该在使用完之后,acquire多少次,release多少次

在这里插入图片描述

conditon,条件满足该怎么样,构造方法传入lock即可,(event,就是条件满足,工人都完成了给boss看到了)
默认传入RLock对象
在这里插入图片描述
等待或者超时
在这里插入图片描述
通知指定的几个
在这里插入图片描述
通知所有
在这里插入图片描述
condition就是条件变化,通知所有人,这样所有人看到这个状态变化的时候就有反应,都在等状态变化,通知正在等待中的线程

condition是用于生产者和消费者模型的,大多数数据都会有产出,就是一个数据生产者,数据处理完,还需要输出,大多数程序都需要输出,所以可以认为大多数程序都是生产者,有了生产者,一个程序处理完,是否还需要其他程序参与,如果要其他程序参与,这个程序就要获得,前面的程序输出的结果,这就变成了生产者和消费者模型,
以前是用队列,来进行解耦

在这里插入图片描述
生产者和消费者模型,还有一种方式是,通知的模式,就算生产者把数据放在队列,那么消费者如何得知,还是需要队列的。以前消费者是看生产者数据是否准备好了,现在是消费者看队列里是否有数据要处理,消费者怎么看队列数据已经好了,时不时看看,或者好了通知你

两种方式,要么主动去查,要么去通知你。生产者和消费者用这种通知机制并不能代替缓冲,数据来了先缓冲一下,但是并不能代替解耦的东西
condition应用在生产者和消费者模型的地方

每一个都有适用,场景,必须掌握在这里插入图片描述
自产自用,生产有生产速度,消费有消费速度,event其实没什么业务能力,就跟time.sleep一样
在这里插入图片描述在这里插入图片描述
等待相应时间
在这里插入图片描述
通过这样就在模拟,消费者消费速度,每隔0.5秒处理一次数据,现在两者数据是不匹配的
在这里插入图片描述
Dspatcher()初始化,然后开启两个线程来使用,让消费者先来,先阻塞一下
在这里插入图片描述
消费者0.5秒一次,生产者1秒一次
报错,补一个括号在这里插入图片描述
现在运行,现在就是,读了一次数据,再一次读取的时候,由于生产慢,等于做了一次无用功
在这里插入图片描述
一般都是先让消费者先启动,因为避免生产者先启动,然后丢失数据,对我们来说数据重要的话,消费者先启动在这里插入图片描述
拿到一个以后,其他两个都是none,queue就是拿了以后,其他没的拿了
在这里插入图片描述
如果要一个数据分发给多个人,这一句就不要了
在这里插入图片描述
现在访问就都一样了
在这里插入图片描述
生产者和消费者的速度,是难以平衡的在这里插入图片描述
默认使用RLock,在这里插入图片描述
做好之后就要通知大家,背后的数据是要等待着的,使用数据的人应该调用wait,生产者生产完数据应该notify,每生产一个数据,应该告诉大家,
可以使用with语法,应该是消费者等生产者通知,
在这里插入图片描述

数据生产好了就要通知大家

用了condition就用到锁了,有个acquire,一定有release,就一定实现了上下文管理,
在这里插入图片描述
通知下面线程不需要等了,都来拿数据,拿到数据消费完之后,就等下一次的通知在这里插入图片描述
把这个event提到外面 在这里插入图片描述
生产的时候可以放外面去,尽量让锁定的时间端一点,生产好了,发通知告诉消费者
在这里插入图片描述在这里插入图片描述
生产者生产10次数据就停止了,消费者在就等待了在这里插入图片描述
如果消费者速度和生产速度差太多,开启多个消费者,用queue,一个拿一个数据来进行处理

这种通知机制,告诉外面,一份数据多人用,也称广播机制在这里插入图片描述
这样就变成5个在这里插入图片描述
通知两个,称为多播,每个任务派两个去做即可
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
一般来讲是消费者先启动

一对全部是广播,一对多是多播在这里插入图片描述

condition

在这里插入图片描述
condition 是用在生产者和消费者中,解决生产者消费者速度匹配的问题,通知机制,强制把速度调成一样了。
使用condition 必须先acquire。用完了要release,内部使用了锁,默认使用RLock,使用锁的最好的方式是with语法,能够保证锁最后一定会被release。
消费者会等待数据准备好,条件成立了就wait,一般都是永久阻塞的,等条件成熟了,就取消
生产者生产好消息,对消息者发通知,可以使用notify或者notify_all方法

在这里插入图片描述
消息队列也有类似的通知机制,尤其是第三方消息队列,都提供了

Rlock的东西其实就是condition也要用,Rlock是线程相关的,在线程中可重入,一定会标记owner是谁,count是多少,另一个线程要用,必须count清零,抢占这把锁,一旦抢占,当前线程可以再次重入获得锁,但是另外的线程要用,就需要清0才可以

lock可以管理你这个线程的,谁占用就是谁的,不释放,别人是用不了这把锁的,要么永久阻塞,跟线程无关,只是同一个lock对象
跟even对象一样

self.condition,self.event这样是同一个对象

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值