Python 3.8异步编程中yield用法及协程手动切换实现方案
如何在Python 3.8异步编程中实现协程主动交还控制权(替代旧版
yield方式) 你提到的问题确实是Python 3.8+异步编程里的常见过渡痛点——旧版本用@asyncio.coroutine装饰器加yield手动挂起协程的方式,在3.8里因为装饰器被弃用、且async def里的yield会生成异步生成器而失效。下面给你两个实用的替代方案:
方案1:用asyncio.sleep(0)(兼容Python 3.8+所有版本)
这是最通用的做法,asyncio.sleep(0)虽然看起来是“睡眠0秒”,但它本质是一个会触发协程挂起的awaitable对象,事件循环会立即把当前协程放到就绪队列的末尾,转而调度其他已经准备好的任务。
示例代码:
import asyncio async def task(): # 执行前置操作 print("Task: 完成第一步操作,准备交还控制权") # 主动让出控制权 await asyncio.sleep(0) # 被事件循环重新调度后,继续执行后续逻辑 print("Task: 重新获得控制权,执行后续操作") async def another_task(): print("Another Task: 趁前面的协程挂起,我开始执行啦") async def main(): # 创建两个并行任务 task1 = asyncio.create_task(task()) task2 = asyncio.create_task(another_task()) # 等待所有任务完成 await task1 await task2 asyncio.run(main())
运行这段代码,你会看到another_task会在task挂起期间被执行,和旧版yield的效果完全一致。
方案2:用asyncio.Task.yield()(Python 3.10+可用)
如果你使用的是Python 3.10或更高版本,可以用asyncio.Task.yield()方法,它的语义更明确,就是专门用来让当前任务主动让出控制权的:
示例代码:
import asyncio async def task(): print("Task: 执行第一阶段逻辑") # 主动让出控制权 await asyncio.current_task().yield() print("Task: 重新被调度,执行第二阶段逻辑") async def main(): t1 = asyncio.create_task(task()) # 创建一个短延时任务,确保在t1让出后能被调度 t2 = asyncio.create_task(asyncio.sleep(0.01, result="次要任务完成")) await t1 await t2 asyncio.run(main())
这个方法比sleep(0)更清晰地表达了“主动交还控制权”的意图,但要注意版本兼容性。
为什么旧版的yield不能用了?
在async def定义的协程中使用yield,Python会把它识别为异步生成器(而非普通协程),异步生成器的作用是异步地产生一系列数据,和我们想要的“手动挂起协程、交还控制权”完全是两个概念,所以不能再用这种方式实现原来的功能。
内容的提问来源于stack exchange,提问作者AdamHommer
相关产品推荐
相关产品推荐

