You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

为何loop.run_forever()会阻塞主线程?asyncio代码执行疑问

问题分析与解答

首先我们来拆解你的代码行为,看看为什么和预期不符:

1. 旧版协程(@coroutine + yield)的运行逻辑

你用的是Python asyncio早期的协程写法,这种写法的协程行为和你的理解有偏差:

  • 当调用coro()时,得到的是一个生成器协程对象loop.run_until_complete()的核心职责是持续调度这个协程,直到它彻底完成(生成器抛出StopIteration
  • 你的coro()里写了while True无限循环,每次yield只是暂时交出控制权,但协程本身并没有结束——事件循环会立刻把它重新调度回来,继续执行下一次循环的printcounter +=1,永远不会停止。

所以你的代码里,loop.run_until_complete(coro())这一行就已经让事件循环无限运行了,后面的loop.run_forever()根本不会被执行到,程序会一直打印Executed0Executed1Executed2...直到你手动终止。

2. 为什么预期结果没出现?

你以为yield交出控制权后,协程就会停止,run_until_complete就会结束,接着执行后续代码——但这是对run_until_complete和旧版协程的误解:

  • run_until_complete不会在协程暂停时停止,只会在协程彻底结束时才会返回。
  • yield@coroutine装饰的生成器里,只是告诉事件循环“我暂时让出CPU,你可以调度其他任务,但我还没做完,记得再叫我”,而不是“我完成了”。

3. 如何修正代码达到预期?

如果你想让协程只执行一次,然后程序输出Finished!,可以做以下修改:

方法1:让协程执行一次后结束

import asyncio
from asyncio.coroutines import coroutine

@coroutine
def coro():
    counter: int = 0
    print("Executed" + str(counter))
    # 去掉while True,让协程执行一次就终止
    yield

loop = asyncio.get_event_loop()
loop.run_until_complete(coro())
# 事件循环在run_until_complete后会自动停止,无需再调用run_forever()
print("Finished!")

方法2:使用现代async/await语法(推荐)

现在Python已经不推荐使用@coroutine和裸yield,更清晰的现代写法是:

import asyncio

async def coro():
    counter: int = 0
    print("Executed" + str(counter))
    # 如果需要临时交出控制权,用await asyncio.sleep(0),这里我们只执行一次
    # await asyncio.sleep(0)

async def main():
    await coro()

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
print("Finished!")

额外说明:关于loop.run_forever()

loop.run_forever()会让事件循环一直运行,直到你主动调用loop.stop()。但它必须在事件循环未运行时调用——如果之前用run_until_complete启动过循环,run_until_complete结束后循环会自动停止,此时再调用run_forever()会重新启动循环并阻塞,直到手动停止。但在你的原代码里,这一步根本没机会执行,因为前面的run_until_complete已经无限运行了。

内容的提问来源于stack exchange,提问作者Notbad

火山引擎 最新活动