具有一个生产者和多个消费者的生产者-消费者

时间:2020-12-27 16:31:18

标签: python python-3.x async-await python-asyncio

我正在尝试通过使用 asyncio 实现生产者-消费者模型,该模型允许生产者将 n 项放入队列,然后创建 n 消费者来处理 {{1} } 项目以并发方式。

这是我目前的尝试:

n

import asyncio import uuid import time import random async def produce(q, interval=1): while True: n = random.randint(0, 20) for _ in range(n): await q.put((uuid.uuid4().hex, time.perf_counter())) await asyncio.sleep(interval) async def consume(item, t): print(f"Managing {item}...") await asyncio.sleep(1) print(f"Item managed after {time.time() - t:.2f} s") async def manage_events(q): while True: item, t = await q.get() print(f"Consuming {item}. In queue: {time.perf_counter() - t}") task = asyncio.create_task(consume(item, time.time()), name=str(item)) print("After manage_item") async def main(): q = asyncio.Queue() await asyncio.gather(produce(q, 2), manage_events(q)) if __name__ == "__main__": s = time.perf_counter() asyncio.run(main()) elapsed = time.perf_counter() - s print(f"{__file__} executed in {elapsed:0.2f} seconds") 秒,创建 0 到 20 个项目并将其放入队列 interval。在函数 q 中,每次从队列中提取一个项目时,都会创建一个异步任务以使用该项目。

这很好用,但我的问题是,当看到并发图时,消耗第一个项目的任务在消耗完成后并没有消亡,因此任务永远以僵尸状态运行,什么也不做,我不明白为什么。预期和期望的行为是任务在函数 manage_events 完成后终止。

---- 编辑---

这是应用user4815162342给出的建议后的代码

consume

事实证明,任务在每次消耗()执行结束时都有效地消亡,但 PyCharm 图形可视化器并未反映这种行为。

1 个答案:

答案 0 :(得分:1)

您的队列架构是非标准的,并没有达到队列的目的。通常,一个队列与固定数量的生产者和固定数量的消费者相关联。每个消费者都在一个循环中获取队列项目,该循环是无限的,或者被取消或哨兵值终止。消费者通常按顺序处理项目,这意味着他们使用 await 而不是 create_task。这可确保您拥有与工作程序数量相匹配的并行性,并且以 FIFO 方式处理项目。

你的队列有一个消费者,它立即产生一个处理异步函数(你称之为消费者),并且不等待它。在这种设置中,您首先不需要队列,您可以直接在生产者中调用 asyncio.create_task(consume(...))。此外,如果您决定使用有界队列来确保背压,那将是无效的,因为 create_task() 只是将工作推送到无界的 asyncio 内部队列。

<块引用>

这很好用,但我的问题是,当看到并发图时,消耗第一个项目的任务在消耗完成后并没有消亡,因此任务永远以僵尸状态运行,什么也不做,

与 POSIX 进程不同,asyncio 的任务没有“僵尸状态”的概念,因此您可能指的是 manage_events 协程,它在无限循环中从队列中获取项目 - 很像一个典型的消费者应该做什么。要在所有项目完成后干净地终止它,您可以使用标记值,例如 None。这意味着在 producer 的末尾可以添加 await q.put((None, None)),在 manage_events 中可以添加 if item is None: break