如何在asyncio中使用阻塞函数

5

我正在一个(不在django内的)项目中使用django ORM。我的工作流程是:

  1. 使用django ORM选择对象
  2. 然后使用一个asyncio库将其发送到消息队列

问题在于你不能在异步环境中调用阻塞函数,也不能在阻塞环境中使用async/await。

我想出了两种解决方案:

  1. 整个程序应该是异步的。然后在需要调用阻塞函数时使用loop.run_in_executor
  2. 整个程序应该是同步的。然后使用asyncio.run()(Python 3.7)来调用所需的异步函数。

我无法决定哪种方法更好。

我知道之前有人问过一个类似的问题(参见此链接)。我的问题是,在尝试结合阻塞和非阻塞代码时是否有一般规则?

1个回答

5
在这两种选择中,我肯定会推荐方案 #1。
#2 的缺点在于将 asyncio 调用拆分为单独的小事件循环运行时,会错过很多 asyncio 功能。例如,您无法创建跨多个调用 asyncio.run() 执行的“后台”任务,而这种任务对于日志记录、监视或超时非常有用。(使用asyncio.run也可能存在性能问题,因为它在每次调用时都创建一个全新的事件循环,但可以通过切换到run_until_complete来解决。)
但是还有第三种选择:
创建一个只执行loop.run_forever()并等待接收工作的单独线程。程序的其余部分由正常的阻塞代码组成,可以使用asyncio.run_coroutine_threadsafe()向 asyncio 请求某些东西。该函数不会阻塞;它立即返回一个concurrent.futures.Future,您可以将其传递并自动等待结果的result()方法。它支持其他功能,例如使用waitas_completed 迭代器等在并行中等待多个实例完成的功能。

在我看来,这种方法结合了问题中两个选项的最佳特点。它使阻塞代码仍然能够等待事件发生、生成线程等等,而不需要强制使用async defrun_in_executor。同时,可以使用异步最佳实践编写asyncio部分,长时间运行的事件循环服务于整个程序。但是,你需要小心,确保从应用程序的其余部分与事件循环进行所有交互(甚至是调用像loop.stop这样简单的函数),都要使用loop.call_soon_threadsafeasyncio.run_coroutine_threadsafe


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接