生产者消费者模型
生产者消费者模型
假设有一个公共队列,生产者向队列中写数据,消费者从队列中读数据。当队列中没有任何数据的时候,消费者应该停止运行并等待(wait),而不是继续尝试读取数据而引发读取空队列的异常。而当生产者在队列中加入数据之后,应该有一个渠道去告诉(notify)消费者。然后消费者可以再次从队列中进行读取,而IndexError不再出现。
from threading import Thread import time import random from queue import Queue # 创建一个队列对象 queue = Queue(10) # 定义一个生产者类,并继承线程类 class ProducerThread(Thread): # 定义自己的run def run(self): nums = range(5) global queue while True: num = random.choice(nums) # 向队列中put一个值 queue.put(num) print("Produced", num) # 睡眠时间,随机1秒之内 time.sleep(random.random()) # 创建一个消费者类,并继承线程类 class ConsumerThread(Thread): # 重新定义run方法 def run(self): # 将队列全局化 global queue while True: # 从队列中get值 num = queue.get() # 一个任务完成 queue.task_done() print("Consumed", num) # 睡眠时间,随机1秒之内 time.sleep(random.random()) ProducerThread().start() ConsumerThread().start()
----------------执行结果-----------------
Produced 1 Consumed-----------> 1 Produced 3 Consumed-----------> 3 Produced 1 Produced 0 Consumed-----------> 1 Consumed-----------> 0 Produced 0
解释:
- 使用Queue实例(下称队列)。
- 这个队列有一个condition,它有自己的lock。如果你使用Queue,你不需要为condition和lock而烦恼。
- 生产者调用队列的put方法来插入数据。
- put()在插入数据前有一个获取lock的逻辑。
- 同时,put()也会检查队列是否已满。如果已满,它会在内部调用wait(),生产者开始等待。
- 消费者使用get方法。
- get()从队列中移出数据前会获取lock。
- get()会检查队列是否为空,如果为空,消费者进入等待状态。
- get()和put()都有适当的notify()。现在就去看Queue的源码吧。
import threading import time import random from threading import Condition queues = [] MAX_NUM = 10 condition = Condition() class ProducerThread(threading.Thread): def run(self): nums = range(5) global queues while True: condition.acquire() if len(queues) == MAX_NUM: print('queues full, producer is waithing!') condition.wait() print('space in queue, consumer notified the producer') num = random.choice(nums) queues.append(num) print('Producer', num) condition.notify() condition.release() time.sleep(random.random()) class ConsumerThread(threading.Thread): def run(self): global queues while True: condition.acquire() if not queues: print('nothing in queue, consumer is waithing') condition.wait() print('producer added something to queue and notified the consumer') num = queues.pop(0) print('consumed--------------->', num) condition.release() time.sleep(random.random()) ProducerThread().start() ConsumerThread().start()
解释:
- 对于消费者,在消费前检查队列是否为空。
- 如果为空,调用condition实例的wait()方法。
- 消费者进入wait(),同时释放所持有的lock。
- 除非被notify,否则它不会运行。
- 生产者可以acquire这个lock,因为它已经被消费者release。
- 当调用了condition的notify()方法后,消费者被唤醒,但唤醒不意味着它可以开始运行。
- notify()并不释放lock,调用notify()后,lock依然被生产者所持有。
- 生产者通过condition.release()显式释放lock。
- 消费者再次开始运行,现在它可以得到队列中的数据而不会出现IndexError异常。
为队列增加大小限制
生产者不能向一个满队列继续加入数据。
它可以用以下方式来实现:
- 在加入数据前,生产者检查队列是否为满。
- 如果不为满,生产者可以继续正常流程。
- 如果为满,生产者必须等待,调用condition实例的wait()。
- 消费者可以运行。消费者消耗队列,并产生一个空余位置。
- 然后消费者notify生产者。
- 当消费者释放lock,消费者可以acquire这个lock然后往队列中加入数据。