上一篇只是大概的讲了一些怎么给数据加锁以及创建线程的两种方式。这次讲一下python中的wait和notify
现在假如有如下情况:
小明:小红
小红:在
小明:我喜欢你
小红:对不起,你是个好人。
对于这种一问一答的方式,我们是否也可以通过加锁来解决呢,我们看代码。
import threading
class XiaoMing(threading.Thread):
def __init__(self,lock):
super().__init__(name='小明')
def run(self):
lock.acquire()
print('{}:小红'.format(self.name))
lock.release()
lock.acquire()
print('{}:我喜欢你'.format(self.name))
lock.release()
class XiaoHong(threading.Thread):
def __init__(self,lock):
super().__init__(name='小红')
def run(self):
lock.acquire()
print('{}:在'.format(self.name))
lock.release()
lock.acquire()
print('{}:对不起,你是个好人'.format(self.name))
lock.release()
if __name__ == '__main__':
lock = threading.Lock()
xiaoming = XiaoMing(lock)
xiaohong = XiaoHong(lock)
xiaoming.start()
xiaohong.start()
这个时候会出现一个现象,我们发现执行的结果是
小明:小红
小明:我喜欢你
小红:在
小红:对不起,你是个好人
很显然,这跟我们想要的结果是不一样的,那么为什么会导致这样的结果呢。
原因就在于我们在小明说完小红的时候会释放锁,接着小明这个线程又拿到了锁,这个时候又继续说了我喜欢你。这就是导致结果跟预期不一致的原因。
因此我们在这里引出了wait和notify这两个方法,这两个方法属于threading的Condition类,condition是一个条件变量,是用来控制复杂的线程之间的同步。
如果看过condition的源码,就会发现condition实现了__enter__ 和__exit__这两个魔术方法,因此我们可以通过with语句来使用condition这个变量。
再说一下wait和notify,wait()只有在被notify唤醒时,才会继续往下执行。因此会有下面这样的代码。
import threading
from threading import Condition
class XiaoMing(threading.Thread):
def __init__(self,condition):
super().__init__(name='小明')
self.condition = condition
def run(self):
with self.condition:
print('{}:小红'.format(self.name))
self.condition.notify()
self.condition.wait()
print('{}:我喜欢你'.format(self.name))
self.condition.notify()
self.condition.wait()
class XiaoHong(threading.Thread):
def __init__(self,condition):
super().__init__(name='小红')
self.condition = condition
def run(self):
with self.condition:
self.condition.wait()
print('{}:在'.format(self.name))
self.condition.notify()
self.condition.wait()
print('{}:对不起,你是个好人'.format(self.name))
self.condition.notify()
if __name__ == '__main__':
condition = threading.Condition()
xiaoming = XiaoMing(condition)
xiaohong = XiaoHong(condition)
xiaohong.start()
xiaoming.start()
运行上面的代码,我们发现执行结果按照我们预期的进行了。需要注意的一点就是start的顺序改了,是小红先start,小明才start。
如果是小明先start的话,那么小红就会在小明notify之后才start,这样小红的wait就收不到小明发过来的信号了,因此会导致
程序一直卡住。
其实这个condition的源码里面,在初始化condition的时候,就会上一把锁,这样另一个线程就进不去with里面了,
而在调用wait的时候,会先把condition时初始化的锁释放掉,然后再分配一把锁到condition的等待队列中,等待notify的唤醒。