import threading
#条件变量condition,用于复杂的线程间同步
class XiaoAi(threading.Thread):
def __init__(self, lock):
super().__init__(name="小爱")
self.lock = lock
def run(self):
self.lock.acquire()
print("{} : 在 ".format(self.name))
self.lock.release()
self.lock.acquire()
print("{} : 好啊 ".format(self.name))
self.lock.release()
class TianMao(threading.Thread):
def __init__(self, lock):
super().__init__(name="天猫精灵")
self.lock = lock
def run(self):
self.lock.acquire()
print("{} : 小爱同学 ".format(self.name))
self.lock.release()
self.lock.acquire()
print("{} : 我们来对古诗吧 ".format(self.name))
self.lock.release()
if __name__ == '__main__':
lock=threading.Lock()
xiaoai=XiaoAi(lock)
tianmao=TianMao(lock)
tianmao.start()
xiaoai.start()
#运行结果:
天猫精灵 : 小爱同学
天猫精灵 : 我们来对古诗吧
小爱 : 在
小爱 : 好啊
我们的期望是,天猫精灵说一句,小爱回复一句。很明显上面的Lock无法实现,需要介绍下面的condition来实现
#通过condition完成协同读诗
class XiaoAi(threading.Thread):
def __init__(self, cond):
super().__init__(name="小爱")
self.cond = cond
def run(self):
with self.cond:
self.cond.wait()
print("{} : 在 ".format(self.name))
self.cond.notify()
self.cond.wait()
print("{} : 好啊 ".format(self.name))
self.cond.notify()
class TianMao(threading.Thread):
def __init__(self, cond):
super().__init__(name="天猫精灵")
self.cond = cond
def run(self):
with self.cond:
print("{} : 小爱同学 ".format(self.name))
self.cond.notify()
self.cond.wait()
print("{} : 我们来对古诗吧 ".format(self.name))
self.cond.notify()
self.cond.wait()
if __name__ == "__main__":
from concurrent import futures
cond = threading.Condition()
xiaoai = XiaoAi(cond)
tianmao = TianMao(cond)
#启动顺序很重要
#在调用with cond之后才能调用wait或者notify方法
#condition有两层锁, 一把底层锁会在线程调用了wait方法的时候释放, 上面的锁会在每次调用wait的时候分配一把并放入到cond的等待队列中,等到notify方法的唤醒
xiaoai.start()
tianmao.start()
#运行结果:
天猫精灵 : 小爱同学
小爱 : 在
天猫精灵 : 我们来对古诗吧
小爱 : 好啊
为什么condition可以使用with方法呢?
因为condition中实现了__enter__
和__exit__
两个协议
过程分析:
xiaoai.start()
(X1)with self.cond # 调用__enter__函数,对_lock加锁
(X2)self.cond.wait() # 1.waiter=Lock(), waiter.require(),2._lock.release() (T1)可以运行了,3.waiter.require(), 线程暂停,等待notify来释放, 4._lock.require(), 线程暂停,等待(T4-2)释放
(X3)print("{} : 在 ".format(self.name))
(X4)self.cond.notify() #waiter.release(),(T4-3)可以运行了,运行到(T4-4)重新获取_lock请求,暂停
tianmao.start()
(T1)with self.cond #_lock.require(),线程暂停,等待_lock释放
(T2)print("{} : 小爱同学 ".format(self.name)) # 天猫精灵 : 小爱同学
(T3)self.cond.notify() #waiter.release(),(X2-3)可以运行了,运行到(X2-4)重新获取_lock请求,暂停
(T4)self.cond.wait() #1.waiter=Lock(), waiter.require(),2._lock.release() (X2-4)可以向下运行了,3.waiter.require(), 线程暂停,等待notify来释放, 4._lock.require()
。。。。以此类推完成一问一答的过程
从上可知,wait函数有四个步骤
1.创建waiter锁,并require()
2.释放底层锁_lock
3.waiter.require()重新加锁
4.重新获取_lock加锁
这里可能会有一个疑问,怎么notify释放的就是上一个waiter锁呢?
因为wait函数调用的时候,把新创建的waiter锁放入到一个双端队列deque中,notify的时候,释放的永远是头端的waiter锁。即实现的是先进先出(FIFO)的数据结构