为什么异步队列等待get()阻塞?

时间:2019-05-30 11:25:50

标签: python-3.x queue python-asyncio producer-consumer

为什么等待queue.get()被阻止?

import asyncio

async def producer(queue, item):
    await queue.put(item)

async def consumer(queue):
    val = await queue.get()
    print("val = %d" % val)

async def main():
    queue = asyncio.Queue()
    await consumer(queue)
    await producer(queue, 1)


loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

如果我在 consumer()之前调用 producer(),则效果很好 也就是说,以下方法可以正常工作。

async def main():
    queue = asyncio.Queue()
    await producer(queue, 1)
    await consumer(queue)

为什么不等待queue.get(),将控制权交还给事件循环,以便生产者协程可以运行,从而填充队列,以便queue.get()返回。 / p>

3 个答案:

答案 0 :(得分:0)

这是因为您调用了await consumer(queue),这意味着直到procuder返回之前,下一行(consumer)才被调用,这当然不会发生,因为还没有人产生

在文档中查看示例,看看它们如何在其中使用:https://docs.python.org/3/library/asyncio-queue.html#examples

另一个简单的例子:

import asyncio
import random


async def produce(queue, n):
    for x in range(1, n + 1):
        # produce an item
        print('producing {}/{}'.format(x, n))
        # simulate i/o operation using sleep
        await asyncio.sleep(random.random())
        item = str(x)
        # put the item in the queue
        await queue.put(item)

    # indicate the producer is done
    await queue.put(None)


async def consume(queue):
    while True:
        # wait for an item from the producer
        item = await queue.get()
        if item is None:
            # the producer emits None to indicate that it is done
            break

        # process the item
        print('consuming item {}...'.format(item))
        # simulate i/o operation using sleep
        await asyncio.sleep(random.random())


loop = asyncio.get_event_loop()
queue = asyncio.Queue(loop=loop)
producer_coro = produce(queue, 10)
consumer_coro = consume(queue)
loop.run_until_complete(asyncio.gather(producer_coro, consumer_coro))
loop.close()

答案 1 :(得分:0)

您应将.run_until_complete().gather()一起使用

这是您更新的代码:

import asyncio

async def producer(queue, item):
    await queue.put(item)

async def consumer(queue):
    val = await queue.get()
    print("val = %d" % val)

queue = asyncio.Queue()
loop = asyncio.get_event_loop()
loop.run_until_complete(
    asyncio.gather(consumer(queue), producer(queue, 1))
)
loop.close()

出局:

val = 1

您还可以将.run_forever().create_task()一起使用

因此您的代码段将是:

import asyncio

async def producer(queue, item):
    await queue.put(item)

async def consumer(queue):
    val = await queue.get()
    print("val = %d" % val)

queue = asyncio.Queue()
loop = asyncio.get_event_loop()
loop.create_task(consumer(queue))
loop.create_task(producer(queue, 1))
try:
    loop.run_forever()
except KeyboardInterrupt:
    loop.close()

出局:

val = 1

答案 2 :(得分:0)

您需要同时启动消费者和生产者,例如像这样定义main

async def main():
    queue = asyncio.Queue()
    await asyncio.gather(consumer(queue), producer(queue, 1))

如果由于gatherconsumer在不同的区域运行而无法使用producer,那么您可以这样做(等效):

async def main():
    queue = asyncio.Queue()
    asyncio.create_task(consumer(queue))
    asyncio.create_task(producer(queue, 1))
    await asyncio.sleep(100)  # what your program actually does
  

为什么await queue.get()不将控制权放回事件循环,以便生产者协程可以运行,从而填充队列,以便queue.get()可以返回。

await queue.get() 正在将控制权交还给事件循环。但是 await 表示 wait ,因此,当您的main协程说await consumer(queue)时,这意味着“ consumer(queue)完成后恢复我。 consumer(queue)本身正在等待某人产生某些东西,您遇到了典型的死锁情况。

撤消订单仅因为您的生产者是一次性的,所以它立即返回给调用者。如果您的生产者正巧等待外部源(例如套接字),那么那里也会有死锁。无论如何编写producerconsumer,并行启动它们都可以避免死锁。