【问题标题】:Juggle Producer and Consumer in Asyncio Python在 Asyncio Python 中兼顾生产者和消费者
【发布时间】:2021-07-12 05:13:46
【问题描述】:

我有一个 producer 和一个 consumer 异步函数。生产者先运行,然后三个消费者同时消费任务。我想无限期地继续这种节奏。

  • 停止生产者并向消费者发送信号以开始运行的最佳方法是什么?
  • 停止消费者并向生产者发送信号以开始运行的最佳方法是什么?

我当前的设置如下所示:

import asyncio
import time


async def producer(event):
    n = 0
    while True:
        print("Running producer...")
        await asyncio.sleep(0.5)
        n += 1
        if n == 2:
            event.set()
            break


async def consumer(event):
    await event.wait()
    print("Running consumer...")
    await asyncio.sleep(0.5)


async def main():
    event = asyncio.Event()

    tasks = [asyncio.create_task(producer(event))] + [
        asyncio.create_task(consumer(event)) for _ in range(3)
    ]

    await asyncio.gather(*tasks)


while True:
    asyncio.run(main())
    print("\nSleeping for 1 sec...\n")
    time.sleep(1)

这会产生以下输出:

Running producer...
Running producer...
Running consumer...
Running consumer...
Running consumer...

Sleeping for 1 sec...

Running producer...
Running producer...
Running consumer...
Running consumer...
Running consumer...

上面的 sn-p 将无限期地运行生产者和两个消费者。这按预期工作:

  • 生产者和两个消费者同时运行
  • 围绕asyncio.runwhile 循环无限期地运行系统

但是,我想知道是否有更好的同步技术来实现这种长时间运行的周期性?

【问题讨论】:

  • 您可以让生产者将值放入队列并让消费者读取它们。另外,不相关,如果您要立即致电gather,则无需使用create_task
  • 感谢create_task 提示。是的,我可以使用队列从中收集价值。但是,我的生产者从 SQS 获取消息,它需要定期运行,以便它可以收集消息以提供给消费者。因此,需要不断地运行生产者和消费者。
  • 生产者可以运行,从 SQS 获取消息,将它们放在asyncio.Queue 中,然后消费者可以获取它们。如果您希望生产者定期运行,可以使用asyncio.sleep。对于消费者来说,如果队列中没有任何东西,他们可以休眠并重试;如果有东西,它可以处理它。
  • @dirn 这是一个有趣的问题。您介意分享一个示例作为答案吗?

标签: python asynchronous async-await io python-asyncio


【解决方案1】:

使用事件唤醒消费者的另一种方法是使用队列同步生产者和消费者。代码看起来像

import asyncio


async def producer(queue: asyncio.Queue):
    while True:
        print("Running producer...")
        message = await fetch_message_from_sqs()
        if message:
            await queue.put(message)
        await asyncio.sleep(0.5)


async def consumer(queue: asyncio.Queue):
    while True:
        print("Running consumer...")
        if queue.empty():
            await asyncio.sleep(1)
            continue
        message = await queue.get()
        print(message)
        await acknowledge_message(message)


async def main():
    # You can set a max size if you want to prevent pull too many messages from SQS.
    queue = asyncio.Queue()

    tasks = asyncio.gather(producer(queue), *[consumer(queue) for _ in range(3)])


if __name__ == "__main__":
    asyncio.run(main())

【讨论】:

  • 检查q.empty() 和睡眠是否必要?它们似乎无缘无故地引入了延迟。
  • @user4815162342 可能不会。 Queue.get 够聪明。从 SQS 获取的函数可能也应该是。
猜你喜欢
  • 1970-01-01
  • 2011-11-08
  • 2011-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多