第39条 用Queue来协调各个线程之间的工作

Python中使用Queue实现多个线程之间的通讯。

例如,现在要构建一个照片处理系统,其分为三个阶段:download、resize、和upload阶段。属于典型的生产者-消费者模型

首先,创建一个自定义的队列数据结构MyQueue

class MyQueue(object):
	'''自定义队列结构
	'''
    def __init__(self):
        self.items = deque()
        self.lock = Lock()  #锁
    ###放入照片
    def put(self,item):
        with self.lock:
            self.items.append(item)
    ###取出照片
    def get(self):
        with self.lock:
            return self.items.popleft()

对于上述三个阶段,定义Worker线程,会从MyQueue队列中取出待处理的任务,并针对该任务运行相关的函数,然后把运行结果放到另一个MyQueue队列中。

class Worker(Thread):	###继承自Thread线程
    def __init__(self,func,in_queue,out_queue):
        super().__init__()
        self.func = func #处理函数
        self.in_queue = in_queue #输入队列
        self.out_queue = out_queue #输出队列
        self.polled_count = 0 #计数
        self.work_done = 0 #处理计数
   	
   	#线程调用此方法
    def run(self):
        while True:	#死循环
            self.polled_count += 1
            try:
                item = self.in_queue.get()
            except IndexError:
                sleep(0.1)
            else:
                result = self.func(item)
                self.out_queue.put(result)
                self.work_done += 1

现在,创建相关的队列,然后根据队列与工作线程之间的对应关系,把三个阶段拼接好。

download_queue = MyQueue()
resize_queue = MyQueue()
upload_queue = MyQueue()
done_queue = MyQueue()
threads = [
 Worker(dowload,download_queue,resize_queue),
 Worker(resize,resize_queue,upload_queue),
 Worker(upload,upload_queue,done_queue)
    ]

启动线程,

for thread in threads:
    thread.start()
for _ in range(1000):
    download_queue.put(object())

上述流程,虽然可以正常工作,但是存在很多缺陷:

  • Workerrun方法处于死循环状态,无法终止。
  • 要想查询任务的进展,必须在最后done_queue上轮询查询已完成的任务数量是否达到指定任务数。
  • 如果管线的某个阶段发生迟滞,那么随时都可能导致程序崩溃。比如,存在某个阶段处理的非常快,导致后续的流程来不及处理,就会导致程序不断的收到大量数据而耗尽内存。

为此,Python提供的Queue类可以解决上述问题,而不是采用自定义的MyQueue类。它具备阻塞式的队列操作,能够指定缓冲区尺寸,而且还支持join方法。下面用Queue类实现相关逻辑。

class ClosableQueue(Queue):
	SENTINEL = object()
	def close(self):
		self.put(self.SENTINEL)
	
	def __iter__(self):
		while True:
			item = self.get()
			try:
				if item is self.SENTINEL:
					return
				yield item
			finally:
				self.task_done()

##根据ClosableQueue的定义修改工作线程
class StoppableWorker(Thread):
	def __init__(self,func,in_queue,out_queue):
		###...
	def run(self):
		### 只要for循环结束,线程就会退出
		for item in self.in_queue:
			result = self.func(item)
			self.out_queue.put(result)
Queue类使用的优点:

1.Queue类使得工作线程无需再频繁的查询输入队列状态,其get方法会持续阻塞,直到有新的数据加入;
2.管线的迟滞问题,Queue类限定队列中最大处理的任务数量解决;
3.同时,Queue类的task_done方法可以追踪工作进度。有了这个方法,就不需要在管线末端的done_queue处进行轮询了。
4.生产者只需要在Queue实例上调用join方法,并等待in_queue结束即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值