11-5 线程同步 - condition使用以及源码分析

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)的数据结构

猜你喜欢

转载自blog.csdn.net/shfscut/article/details/80450076