生产者与消费者模型是最为常见的多线程编程问题, 本文使用python语言利用Condition线程同步技术来解决生产者与消费者问题, 在这个过程中, 你将学习了解到多线程如何进行同步
生产者与消费者模型是最为常见的多线程编程问题,它可以简要的总结为以下内容:
商品池中的商品数量是动态变化的,学习生产者和消费者模型,你需要关心以下几个问题:
线程同步的意思不是几个线程同时进行某个操作,而是指线程之间协同步调,这个“同”字是协同的意思,而不是同时的意思,在本例的生产者与消费者模型中,我希望能做到这样的同步(你也可以设计你的同步方式,如何同步,不是固定死的套路):
一个生产者生产商品后,能够进入到等待状态,由其他生产者或者消费者进行生产或消费操作
一个消费者消费商品后,能够进入到等待状态,由其他消费者或者生产者进行消费或生产操作
Condition被称为条件变量,除了提供与Lock类似的acquire和release方法外,还提供了wait和notify方法,如果你已经对锁有一定了解,那么,你对acquire和release肯定不陌生了,我重点介绍一下wait和notify方法
调用这个方法,会释放掉底层的锁,那么看来,此前,一定是得到锁了。没错,使用condition的第一步就是调用acquire方法,这一步已经获得了锁。我们可以认为condition对象维护了一个锁,如果你不指定这个锁,默认是RLock锁,同时还维护了一个waiting池,调用wait方法后,waitting池会记录这个线程,同时,这个线程也进入到了阻塞状态,直到超时或者别的线程唤醒它。
当这些线程被唤醒以后,会重新试图去获得锁
这个方法会唤醒处于waiting状态的线程,能唤醒多少个呢?这取决于传入的参数,如果不传,默认唤醒其中一个,如3.1中所说,被唤醒的这个线程会再次acquire锁,得到锁以后继续执行
代码并非我原创,而是从网上抄录下来的一篇,我重点来解释代码
import threading
import time
condition = threading.Condition()
products = 12
class Producer(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
global condition, products
while True:
if condition.acquire():
if products < 10:
products += 1;
print("Producer(%s):deliver one, now products:%s" %(self.name, products))
condition.notify()
else:
print("Producer(%s):already 10, stop deliver, now products:%s" %(self.name, products))
condition.wait();
condition.release()
time.sleep(2)
class Consumer(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
global condition, products
while True:
if condition.acquire():
if products > 1:
products -= 1
print("Consumer(%s):consume one, now products:%s" %(self.name, products))
condition.notify()
else:
print("Consumer(%s):only 1, stop consume, products:%s" %(self.name, products))
condition.wait()
condition.release()
time.sleep(2)
if __name__ == "__main__":
for p in range(0, 2):
p = Producer()
p.start()
for c in range(0, 10):
c = Consumer()
c.start()
在这个例子中,我把products的初始值设置为12,这样,两个生产者启动以后,都会调用wait方法,我这样设计的目的是想告诉你,wait的作用是释放掉底层的锁,只有这样,消费者线程启动以后,再调用acquire时才会成功获得锁,千万不要误以为wait会一直把持锁,实际上它释放了锁,然后等待被唤醒
因为一次只有一个线程能够得到这把锁,其他线程都处于waiting状态,因此不论是生产者也好,还是消费者也罢,在自己完成生产或消费活动后,都要调用notify方法,唤醒其他线程,假设A线程调用了notify,唤醒了B线程,那么B线程会立刻获得锁么?答案是不会
因为此时,锁依然掌握在A线程手中,要注意,notify方法并不会归还锁,它只是唤醒其他线程,要等到A线程release时才会真正的释放掉锁,这时,B线程才会得到锁,如果A线程调用的是nofity(3),那么同时有3个线程被唤醒,这3个线程会争抢锁,最终也只有一个会获得锁
我期初对这两个东西是比较困惑的,他们都释放了底层的锁,那么调用完了wait,是不是就可以不用再调用release了呢?答案是不可以
wait的确是释放了底层锁,这个作用和release是一样的,但是,调用wait后,线程就阻塞在那里了,一直等到被唤醒才会继续执行之后的代码,可以被唤醒就意味着要去获得锁,如果得不到锁,会继续尝试获得锁,如果得到锁,就必须用release来释放掉锁
严格的讲,一个acquire必须对应一个release,他们必须成对出现,切记
QQ交流群: 211426309