在这个简单的生产者/消费者示例中,好像await queue.put(item)
不会释放事件循环以允许使用者运行直到完成。这导致生产者将所有物品放入队列,然后消费者才能将它们取走。
这是预期的吗?
如果我将await queue.put(item)
跟随await asyncio.sleep(0)
,则会得到想要的结果。
然后,生产者将1个项目放入队列,而消费者则将1个项目从队列中移出。
我在Python 3.6.8和3.7.2中得到相同的结果。
import asyncio
async def produce(queue, n):
for x in range(1, n + 1):
print('producing {}/{}'.format(x, n))
item = str(x)
await queue.put(item)
# await asyncio.sleep(0)
await queue.put(None)
async def consume(queue):
while True:
item = await queue.get()
if item is None:
break
print('consuming item {}...'.format(item))
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()
答案 0 :(得分:3)
这导致生产者将所有物品放入队列,然后消费者才能将它们取走。可以吗?
是的。问题在于您的队列是无限制的,因此将其放入队列永远不会使生产者挂起,因此也不会屈服于其他协程。对于立即提供数据的所有等待者(例如read at EOF。
如果生产者循环包含另一个暂停源,例如等待实际输入(毕竟它必须从某个地方获取物品),那么这将导致它暂停并且问题不会立即引起注意。使用asyncio.sleep(0)
的强制暂停也可以使用,但是它很脆弱,因为它依靠单个暂停来运行使用者。情况并非总是如此,因为消费者本身可以等待队列以外的其他事件。
在某些情况下,无限制队列是有意义的,例如当队列中预先填充任务时,或者生产者的体系结构将项目数限制为合理的数时。但是,如果队列项目是动态生成的,则最好添加一个界限。边界保证了生产者的背压,并确保它不会垄断事件循环。