Python3 asyncio 如何开启一个不 await 的协程

用 aiohttp 框架,当一个 web 请求过来时,我需要开启一个协程把请求的一些信息记录到数据库中,但又不是很重要,所以不用管是否成功,不成功报个错就可以。但我不想这个过程影响 web 返回的时间,所以我不想 await 它完成,甚至在 web 返回后再处理这个就行,这个要怎么办。

即使 loop.run_in_executor ()也要 await。。。

来自一个中了 golang 的毒的人。


Python3 asyncio 如何开启一个不 await 的协程
11 回复

loop.create_task

但你这么做不合理


在Python的asyncio里,直接创建一个协程但不await它,通常意味着你想让它“在后台”运行。这其实是个常见的需求,比如处理日志、发送心跳包,或者执行一些不需要立即知道结果的任务。

最直接、最推荐的做法就是用asyncio.create_task()。这个函数会把你的协程包装成一个Task对象,并立刻排入事件循环的调度队列。事件循环会在合适的时机自动执行它,你完全不用手动去await

import asyncio

async def my_background_task():
    print("后台任务开始")
    await asyncio.sleep(2)
    print("后台任务结束")

async def main():
    # 关键在这里:create_task 后不 await
    task = asyncio.create_task(my_background_task())
    print("主协程继续执行,不等待后台任务")
    # 主协程可以干自己的事
    await asyncio.sleep(1)
    print("主协程做了点别的事")
    # 最后可以选择等待后台任务完成(可选)
    await task

# 运行
asyncio.run(main())

运行这段代码,你会看到输出是交错进行的:

主协程继续执行,不等待后台任务
后台任务开始
主协程做了点别的事
后台任务结束

核心要点:

  • asyncio.create_task(coro()):这是标准答案。它创建任务,立即返回一个Task对象,不会阻塞当前协程。
  • 任务对象:这个Task对象你可以存下来(比如放到一个列表里),以后如果需要可以再await它来等待结果或捕获异常。如果完全不关心结果,也可以不管它。
  • ensure_future的区别:老教程里可能用asyncio.ensure_future(),在Python 3.7+里,create_task()是更明确、更推荐的方式。

一句话总结:用 asyncio.create_task() 来启动不阻塞当前流程的协程。

新起一个线程,在那个线程中跑新的 event_loop,不重要的任务可以放在那个里面处理。

开一个线程专门处理?

傻了,看的例子都是 create_task,然后在 await,文档上明明写了会排期准备执行,被我忽略了。这么做有啥不合理?

同步思维里会开个线程丢到后台里跑
异步里面, 旧版本使用 asyncio.ensure_future, 新版本支持 create_task
简单的说就是一个协程函数 -> Task 的过程, 就是让它去执行的过程, 不变 Task 就不会执行, Task 和协程都可以被 await

至于上面说的那些个起一个线程的是什么鬼我也不知道, 但是线程有个好处就是有个 daemon 参数, 这个参数控制的是主线程在运行完毕后是否等待这个在跑的子线程跑完再退出程序. asyncio 的 Task 有没有这功能不太确定, 一般情况下会被直接 cancel 丢弃

开一个新线程没多大用吧,开就开一个进程,这样才能利用多核,loop.run_in_executor ()这个就可以开一个新的进程,运行 cpu 阻塞函数。我主要卡在 await 这个了,以为都要 await。

明白,我主线程永不退出,除非关机。

我也是这个场景下用的, 不过一般会带 timeout 跑, 超时 cancel
理解协程比较粗糙的想法就是
协程函数 ->

协程(相当于一个迭代器但是没开始迭代) + Future (相当于一个 awaitable 的对象, 在 set_result 的时候标记状态为 done, 结束 await, 很多语言里有这种 Future 概念) ->

组成一个 Task(Task 是 Future 子类) ->

Task 里如果没指定 Loop, 会在 get_event_loop 得到的那个里面开始执行, 主要注意两个问题, 1 不能在 difference Loop 执行的报错 2. Loop 已经 running ->
Task 可以被 wait, 这里可以设置超时; 也可以被 await, 也有办法设置超时, 阻塞该行代码, 不阻塞主线程; 要注意有的 timeout 会 cancel, 有的不会;

协程对线程最优良的一个地方就是, 随时可以中断, 线程想中断太难了

我是说立刻返回结果给用户不合理。

因为这样可能导致大量用户得到了正反馈,然而你其实一个都没执行完。。。

其实你要的是异步定时任务罢了

回到顶部