生产者-消费者简单理解:生产者相当于厨师,消费者相当于顾客,容器相当于出餐窗口,或者相当与放餐的一个东西。但是这个东西放餐的位置是有顺序的。而厨师跟消费生产与用餐是没有顺序的。kakfa这种是有多个容器的发布订阅模式。
python 可以与redis, Rabbitmq,kafka 等实现生产者消费者模式,但是python还可以使用队列实现生产者与消费者模型。
1、首先了解Queue(队列)的数据结构形式,队列一般用排队的方式进行比喻,画一下队列的示意图。先进先出, python的Queue是怎样做此功能的呢? 也是一样的。
2、生产者容器队列消费者三者关系图,图形如下。
4、首先创建第一个队列Q1,用来模拟多线程生产者要 生产的数据, 创建1000个数字 0-999,放入队列中。其次再次创建一个队列Q2,这个用来模拟redis或者rabbitmq的订阅模式的容器,来存放生产者生产的数据,与消费者要消费的数据。
import queue
from threading import Thread
Q1 = queue.Queue()
for i in range(1000):
Q1.put(i)
Q2 = queue.Queue()
5、创建生产者Producer,这里可以使用多线程,继承多线程的方式
class Product(Thread):
def __init__(self, Queue):
super(Product, self).__init__()
self.queue = Queue # 这个Queue是Q2
def general_data(self):
data = Q1.get()
self.queue.put(data)
print(f'name: {threading.current_thread().name} 生成一条数据',data)
def run(self):
while True:
if self.cli.qsize() > 10000:
break
else:
self.general_data()
print(f'name: {threading.current_thread().name} is over!!!')
if __name__ == '__main__':
thread_list= []
for i in range(3):
producer = Product(Q2)
thread_list.append(producer)
producer.start()
# 阻塞生产者线程
for t in thread_list:
t.join()
打印结果:
6、创建消费者,消费者的代码跟生产者的代码差不多,因为都是多线程多线程
class Consumer(Thread):
def __init__(self, Queue):
super(Consumer, self).__init__()
self.queue = Queue
def get_data(self):
data = self.queue.get()
if data is None:
return None
else:
print(f'name: {threading.current_thread().name} 消费1条数据', number)
def run(self):
while True:
self.get_data()
if self.queue.qsize() == 0:
break
print(f'name: {threading.current_thread().name} 消费完了!!!')
if __name__ == '__main__':
for i in range(3):
consumer = Consumer(Q2)
consumer.start()
打印结果:
注意点:使用队列也是可以进行生产者与消费者模式进行模拟的,只不过,出与数据的安全问题,一般不适用队列Queue,一般使用redis,rabbitmq,这些中间,因为这些中间件是可以将数据进行持久化的,来保证数据的安全。也是因为,生产者与消费者一般是两个进程进行执行的,又因为进程是无法进行通信的,线程之间是可以进行通讯的,所以可以用队列来在一个模块中模拟。如果将生产者与消费者写到两者文件中,是没办法进行模拟的,自己动手可以试一下。