为何asyncio队列的await queue.get()会阻塞?代码执行顺序疑问
为什么先调用consumer()再调用producer()时asyncio队列会阻塞?
咱们先看你给出的两种场景:
阻塞的代码场景
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()
正常运行的代码场景
async def main(): queue = asyncio.Queue() await producer(queue, 1) await consumer(queue)
你的疑问是:为什么await queue.get()没有把控制权交还给事件循环,让producer协程运行填充队列?
其实问题的核心不是queue.get()没交还控制权,而是main协程的执行顺序限制了producer的启动时机,咱们一步步拆解:
- 在阻塞场景的
main里,你是顺序await的:先执行await consumer(queue),这会让main协程暂停,直到consumer协程完全执行完毕,才会去执行下一行的await producer(queue,1)。 - 当consumer协程启动后,遇到
await queue.get()——因为队列是空的,consumer会暂停并把控制权交还给事件循环。但这时候事件循环里,只有main协程在等着consumer结束,producer还没被创建和调度呢!毕竟main要等consumer跑完才会去调用producer。 - 事件循环里没有其他可运行的协程,只能一直等着consumer的
queue.get()拿到数据,但永远等不到——因为producer根本没机会启动往队列里放数据,这就造成了阻塞。
打个比方:你让家人先去快递站拿快递,但你要等家人拿到快递回来,才会联系快递员发快递,这不就卡死了嘛!
而反过来的正常场景里,你先让producer把数据放进队列,再让consumer去拿,自然顺理成章,不会有问题。
怎么让先consumer再producer也正常运行?
如果想让consumer和producer同时运行,不能用顺序await,得让它们并发执行,比如用asyncio.gather():
async def main(): queue = asyncio.Queue() await asyncio.gather( consumer(queue), producer(queue, 1) )
这样事件循环会同时调度两个协程:consumer卡在queue.get()时,producer会被执行并把数据放进队列,consumer拿到数据后继续执行,程序就能正常结束了。
内容的提问来源于stack exchange,提问作者Akshay Takkar
相关产品推荐
相关产品推荐

