今天三个部分的知识、递归锁(解决同步锁造成的锁死现象)、同步对象(类似一个锁,使两个线程之间进行同步)、信号量(控制线程数量的一个锁),悄悄的说一下,算上昨天的同步锁,虽然已经讲了四种锁了,但是据说总共是有五种锁的,最后一个我还不知道是个啥。
一、递归锁
昨天引入了同步锁的概念,但是因为同步锁的引入,又发生了一个问题,就是两个锁来回锁,导致程序最终被锁死,你等我我等你,谁都没法运行了,先看一下锁死的情况吧,情况如下:
import threading,time class myThread(threading.Thread): def doA(self): lockA.acquire() print(self.name,"gotlockA",time.ctime()) time.sleep(3) lockB.acquire() print(self.name,"gotlockB",time.ctime()) lockB.release() lockA.release() def doB(self): lockB.acquire() print(self.name,"gotlockB",time.ctime()) time.sleep(2) lockA.acquire() print(self.name,"gotlockA",time.ctime()) lockA.release() lockB.release() def run(self): self.doA() self.doB() if __name__=="__main__": lockA=threading.Lock() lockB=threading.Lock() threads=[] for i in range(5): threads.append(myThread()) for t in threads: t.start() for t in threads: t.join()
大致说一下上面锁死的情况,还是画一个图吧,公文写的太多,现在一看到文字描述就会感觉自己词穷了:
当线程1的doA操作完成后,两把锁均被打开,此时就有可能切换到线程2上,此时第一个线程获得了B锁,而第二个线程几乎同时获得了A锁,然后尴尬的事情发生了,线程1需要的A锁被线程2抢了,线程2需要的B锁被线程1抢了,然后都不愿意,最后就锁死了。
所以为了解决上面的问题,引入了递归锁:
rlock = threading.rLock( )
为了支持在同一线程中多次请求同一资源,python提供了“可重入锁”:threading.RLock。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源。
把上面刚才有问题的AB锁全部替换成为rlock,就可以解决这个问题了,然后会发现,他会doA执行完全部执行另一个线程的doB或者下一个doA,不会发生锁死
引入一个案例吧!
import time import threading class Account: def __init__(self, _id, balance): self.id = _id self.balance = balance self.lock = threading.RLock() def withdraw(self, amount): with self.lock: self.balance -= amount def deposit(self, amount): with self.lock: self.balance += amount def drawcash(self, amount):#lock.acquire中嵌套lock.acquire的场景 with self.lock: interest=0.05 count=amount+amount*interest self.withdraw(count) def transfer(_from, to, amount): #锁不可以加在这里 因为其他的其它线程执行的其它方法在不加锁的情况下数据同样是不安全的 _from.withdraw(amount) to.deposit(amount) alex = Account('alex',1000) yuan = Account('yuan',1000) t1=threading.Thread(target = transfer, args = (alex,yuan, 100)) t1.start() t2=threading.Thread(target = transfer, args = (yuan,alex, 200)) t2.start() t1.join() t2.join() print('>>>',alex.balance) print('>>>',yuan.balance)
二、同步对象(Event)
啥是同步啊,昨天说的,就想io输入,我等着你就是同步,异步就是我先走了,等你搞好了再回来接你
直接上案例吧,下面这个就是使用Event实现的同步,员工等着老板发话。
import threading,time class Boss(threading.Thread): def run(self): print("BOSS:今晚大家都要加班到22:00。") print(event.isSet()) event.set() time.sleep(5) print("BOSS:<22:00>可以下班了。") print(event.isSet()) event.set() class Worker(threading.Thread): def run(self): event.wait() print("Worker:哎……命苦啊!") time.sleep(1) event.clear() event.wait() print("Worker:OhYeah!") if __name__=="__main__": event=threading.Event() threads=[] for i in range(5): threads.append(Worker()) threads.append(Boss()) for t in threads: t.start() for t in threads: t.join()
通过event = therading.Event( ) 然后通过event.wait()等待一个状态
此时event.isSet的状态是False,然后上面这个wait就会一直等着
然后发生了event.set(),通过set,是原来的False变成了True
这个时候event.wait()就可以开始正常执行了
event.clear()能够使event的状态重新回到False
这里应该注意的点: event的状态是在各个线程内都是有效地,状态通用
三、信号量(Semaphore)
信号量用来控制线程并发数的,BoundedSemaphore或Semaphore管理一个内置的计数 器,每当调用acquire()时-1,调用release()时+1。
计数器不能小于0,当计数器为 0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。(类似于停车位的概念)
BoundedSemaphore与Semaphore的唯一区别在于前者将在调用release()时检查计数 器的值是否超过了计数器的初始值,如果超过了将抛出一个异常。
这个内容好像没怎么重点说,可能在后期具体使用的时候才会具体用到,直接看案例:
import threading,time class myThread(threading.Thread): def run(self): if semaphore.acquire(): print(self.name) time.sleep(5) semaphore.release() if __name__=="__main__": semaphore=threading.Semaphore(5) thrs=[] for i in range(100): thrs.append(myThread()) for t in thrs: t.start()
这样每次都只会蹦出5个线程
今天打算努力回忆一下之前的知识,但是发现真的是忘的精光,最近脑子也是极其的不好使,周末开始复习,不放风筝。