目录
前面两篇大致介绍了生产者消费者模式及python操作多线程的基本操作,这篇就把两者结合。
上一篇中举例用的苹果id,但是实际生活中,去超市买水果,消费的可能是苹果,也可能是梨,所以单纯生产苹果id就满足不了需求了。我考虑的是封装一个生产者父类及一个消费者父类。生产者父类定义生产消费者对象放入队列的方法,消费者父类定义从队列取出消费对象进行消费,针对不同类型产品子类可继承父类中的方法进行生产消费。除此之外,还对单个生产者线程及消费者线程进行封装,定义这两种不同的线程运行规则,如何进行生产与消费。
流程图
代码实现
生产者基类——BaseProducer
# coding=utf-8
class BaseProducer(object):
def get_queue_items(self):
'''
@return: 生产者基类,需要字类实现逻辑
'''
pass
消费者基类——BaseConsumer
# coding=utf-8
class BaseConsumer(object):
def __init__(self):
# 消费者线程名称
self.consumer_thread_name = ' '
def action(self):
pass
消费者线程封装——ConsumerThread
由于生产者线程初始化的时候会初始化消费者线程,所以先封装消费者线程
# coding=utf-8
import threading
class ConsumerThread(threading.Thread):
def __init__(self, queue, consumer_thread_name, consumer_sleep_time):
'''
@param queue: 生产者初始化的队列
@param consumer_thread_name: 消费者线程名称
@param consumer_sleep_time: 消费者等待实践
'''
# 初始化线程
super(self.__class__, self).__init__()
self.queue = queue
self.name = consumer_thread_name
self.consumer_sleep_time = consumer_sleep_time
def run(self):
'''
从队列中取出消费对象,并调用消费对象方法action进行消费
'''
while True:
consumer = self.queue.get()
# 将当前线程名称与消费者对象绑定
consumer.consumer_thread_name = self.name
print("消费线程是:%s" % consumer.consumer_thread_name)
consumer.action()
self.queue.task_done()
生产者线程——ProducerThread
# coding=utf-8
from demo.base_producer import BaseProducer
from demo.consumer_thread import ConsumerThread
import threading
import time
class ProducerThread(threading.Thread):
'''
生产者线程
'''
def __init__(self, queue, producer_thread_name, producer, consumer_maxnum, producer_sleep_time,consumer_sleep_time):
'''
@param queue: 队列对象
@param producer_thread_name: 队列名称
@param producer: 生产者实例对象
@param consumer_num: 消费者最大线程数
@param producer_sleep_time:生产间隔
@param consumer_sleep_time:消费间隔
'''
# 初始化线程
super(self.__class__, self).__init__()
self.queue = queue
self.producer_thread_name = producer_thread_name
self.producer = producer
self.consumer_maxnum = consumer_maxnum
self.producer_sleep_time = producer_sleep_time
self.consumer_sleep_time = consumer_sleep_time
# 如果初始化出来的对象不是Producer类,则抛出异常
if not isinstance(producer, BaseProducer):
raise Exception("%s is not ProducerAction instance" % producer)
print("init producer_%s"%producer_thread_name)
def run(self):
# 定义一个列表,用来装生产出来的消费对象
item_list = []
while True:
# 如果列表中中消费对象全部进入队列中,则重新开始生产
if len(item_list)==0:
print("列表消费者对象为空,开始生产消费对象")
item_list = self.producer.get_queue_items()
# 记录生产的消费对象数量
total_num = len(item_list)
while True:
# 如果队列为空,则跳出循环
if len(item_list)==0:
break
# 当队列中未取出的消费行为对象数量小于等于消费对象最大数量,则添加入队列
if self.queue.unfinished_tasks <= self.consumer_maxnum:
# 列表中弹出一个消费对象
consumer_tmp = item_list.pop()
# 加入队列
self.queue.put(consumer_tmp)
# 生产每个消费对象之间的间隔
time.sleep(self.producer_sleep_time)
def start_work(self):
'''
生产者开始工作
'''
# 根据最大消费对象线程数量创建消费对象
for i in range(1,self.consumer_maxnum+1):
print("init and start consumer_%s%d" % (self.producer_thread_name, i))
# 创建消费对象线程
consumer = ConsumerThread(self.queue, "%s_%d"%(self.producer_thread_name,i), self.consumer_sleep_time)
# 开始消费
consumer.start()
self.start()
消费苹果业务
# coding=utf-8
from demo.base_producer import BaseProducer
from demo.base_consumer import BaseConsumer
from demo.producer_thread import ProducerThread
import queue
class AppleProducer(BaseProducer):
def get_queue_items(self):
# 一次生产10个消费对象,返回列表
consumer_list = []
for i in range(1,11):
consumer = AppleConsumer("apple_id_%d"%i)
consumer_list.append(consumer)
return consumer_list
class AppleConsumer(BaseConsumer):
def __init__(self,apple_id):
super(self.__class__, self).__init__()
self.apple_id = apple_id
def action(self):
print("消费的苹果是:%s"%self.apple_id)
if __name__ == "__main__":
# 初始化生产者对象
producer = AppleProducer()
queue = queue.Queue()
producer_thread = ProducerThread(queue, "apple_thread", producer, 5, 0.1, 1)
producer_thread.start_work()
结果
从结果来看,先初始化了一个苹果生产者线程,接着生产线程里初始化了5个消费线程,然后生产线程生产了10个消费对象放列表并添加到队列,5个消费线程分别取消费对象进行消费,当生产的列表中对象个数为0时,重新开始生产消费对象。
总结
主业务可以自己写好自定义的Producer和Consumer,然后放到线程中。后续可以在多台机器上跑多个生产线程,队列用mysql或者redis代替。