运行中途创建非阻塞并行asyncio任务的标准实现方法
问题背景
我的设计方案如下:
- 任务1持续休眠并检测来自用户界面的输入;
- 任务2反复等待温度传感器的输入数据。
目前遇到的问题:用户有时会按下按钮触发一个延后执行的动作,现有相关代码如下:
def completion(): # do something async def longtask(): asyncio.sleep(5) completion()
我希望在任务1中实现如下逻辑:
async def task1(): while True: await asyncio.sleep(0.1) foo = check_input() if foo: notawait longtask() # other processing
这里的notawait具体含义是:将longtask加入调度队列,但不会暂停task1等待其执行完成;当task1处于休眠状态时,longtask可被调度执行。也就是说在longtask执行结束前,程序中会同时存在3个活跃任务,而非原先的2个。
标准实现方法
使用asyncio.create_task()即可,这是Python 3.7+版本提供的官方标准高层API,专门用于将协程注册到当前运行的事件循环中作为后台任务调度,完全匹配你描述的非阻塞提交需求。
你现有代码存在两处明显问题:
longtask()中的asyncio.sleep(5)前缺少await关键字,休眠逻辑不会生效,会直接跳过等待执行completion()- 直接调用协程函数只会生成一个未被调度的协程对象,不会进入事件循环执行,必须包装为Task对象才会被调度运行。
修正后的核心代码如下:
import asyncio def completion(): # 实际业务逻辑 pass async def longtask(): await asyncio.sleep(5) completion() async def task1(): while True: await asyncio.sleep(0.1) foo = check_input() if foo: # 提交后台任务,不阻塞当前task1执行 asyncio.create_task(longtask()) # 其他处理逻辑
行为说明
- 调用
asyncio.create_task(longtask())时,会立刻将longtask包装为Task实例加入事件循环调度队列,当前task1不会在此处暂停,会直接继续执行后续逻辑 - 当task1执行到下一轮
await asyncio.sleep(0.1)让出事件循环控制权时,新建的longtask任务就会被调度执行 - 只要不主动await这个新建的Task对象,longtask就会和原有的task1、task2以协作式并发的方式运行,运行期间活跃任务数为3,和你的预期完全一致。
注意事项
- 如果需要捕获后台任务的异常、获取执行结果,可以将
create_task返回的Task对象存储到变量/集合中,避免Task被垃圾回收导致意外终止;不需要感知执行结果的场景下可以直接调用不保存返回值 - Python 3.7之前的版本可以通过获取当前运行的事件循环实例,调用
loop.create_task()实现相同效果,目前官方优先推荐直接使用asyncio.create_task()。
内容的提问来源于stack exchange,提问作者Joymaker
相关产品推荐
相关产品推荐

