在Python asyncio应用中优雅地运行后台协程任务

说实话,当时我不太明白……但后来我想通了,写下来以防我忘记。

Asyncio 使用起来非常方便,但是如果后台任务运行不正常,就会出现问题。
比如你直接使用threading.Thread来运行一个异步函数,它会直接返回,不等你等待。
这肯定会触发警告。

最直接的方法:
python 背景线程 = 线程. 线程( 目标= asyncio.run, args=(background_task(),),一定要加上括号。
直接传递函数名是行不通的。
恶魔=真 ) background_thread.start()
asyncio.run 本身可以创建一个独立的事件循环,而不干扰主循环。
设置daemon=True,这样当主程序退出时,子线程也退出,资源不会滞留。

但是,您不能在协程内执行任何过于严格的操作,例如 time.sleep。
你需要使用这个:
python 等待 asyncio.sleep(0.1 )
否则整个事件循环将会卡住。
必须正确处理异常,否则后台任务会在不知不觉中挂起。
我以前也遇到过问题。
后台任务挂了很久,客户端崩溃了,我一头雾水。

至于传递数据,如果主线程向后台线程传递数据,则使用Queue.Queue即可。
这个东西是线程安全的。
如果后台线程想要返回主线程,则必须使用asyncio.Queue或loop.call_soon_threadsafe()。

关闭时要小心。
你不能直接杀死一个线程。
您需要使用事件:
python stop_event = threading.Event()
异步 def background_task(stop_event): 而不是 stop_event.is_set(): 任务逻辑 等待 asyncio.sleep(1 )
stop_event.set()
启动线程时传递stop_event。
当主程序即将停止时设置此事件。

简而言之,使用 asyncio.run 在独立线程中运行协程可以解决您提到的警告,并防止后台任务干扰主程序。
此方法对于 FastAPI 和 Uvicorn 都适用。
只有几个关键:你需要能够使用 asyncio.run,你需要在协程中等待,并且你需要确保正确的通信线程和正常关闭之间。

在python中线程和协程的区别是什么

说白了,Python 中的线程和协程差异太大,无法实现相同的货币。
其实很简单。
线程和协程的区别主要体现在以下几个方面。

首先,最重要的是,由于一个线程可以有多个协程,因此在Python中可以更好地利用多核CPU。
比如说我们去年做的项目,规模大概是3 000左右。
通过在单个线程中管理多个协程,我们能够高效地运行多核应用程序。

还有一点就是线程是同步机制,而协程是异步的。
这意味着执行完一个任务后,线程需要执行另一个任务,协程可以切换到另一个任务执行,而不会阻塞线程。
用行话来说,这称为雪崩效应。
事实上,前面的一个小小的延迟就可以拖垮后面的一切,而套路可以有效避免这种情况。

还有一个更重要的细节。
协程可以保留上次调用的状态。
每次进程重新进入时;这相当于进入了上次通话状态。
虽然我一开始认为这会让状态管理变得复杂后来Python的例程设计得相当巧妙,使得状态管理变得非常简单。

最后,一个实用的建议是如果您正在运行 IO 密集型任务,例如网络请求。
您可以使用多线程。
如果您正在进行复杂计算等CPU密集型工作。
您可以考虑使用多个进程。
当重点是不受阻碍的平行竞争对手时。
定期活动是一个不错的选择。
等等还有一件事。
尽管协程最常用于 Web 应用程序,但它们在其他情况下也可以发挥重要作用。

总之,根据您的需求选择合适的货币机制。
我认为值得一试。