简体   繁体   English

如何使用asyncio阻止函数

[英]How to use blocking functions with asyncio

I'm using django ORM in a project(outside of django). 我在一个项目中使用django ORM(在django之外)。 My workflow is 我的工作流程是

  1. selecting objects by the django ORM and 通过django ORM选择对象
  2. then send it to a message queue using an asyncio lib 然后使用asyncio lib将其发送到消息队列

The problem is you can't call blocking functions in async environment and you can not use async/await in blocking environment. 问题是您无法在异步环境中调用阻塞函数,也无法在阻塞环境中使用async / await。

I have come up with 2 solutions: 我想出了两个解决方案:

  1. The whole program should be async. 整个程序应该是异步的。 And use loop.run_in_executor to call blocking functions when needed. 并在需要时使用loop.run_in_executor调用阻塞函数。

  2. The whole program should be sync. 整个程序应该是同步的。 And use asyncio.run() (Python 3.7) to call async functions needed. 并使用asyncio.run() (Python 3.7)来调用所需的异步函数。

I can't decide which one is better approach. 我无法决定哪一种方法更好。

I know a similar question has been asked before. 我知道以前曾问过类似的问题 My question is is there a general rule when trying to combine blocking and non-blocking code? 我的问题是在尝试组合阻塞和非阻塞代码时是否有一般规则?

Given the choice between those two, I would definitely recommend approach #1. 鉴于这两者之间的选择,我肯定会建议方法#1。

#2 has the downside that you're missing out on a lot of asyncio functionality by splitting up asyncio calls into separate little event loop runs. #2有一个缺点,你错过了很多asyncio功能,将asyncio调用分成单独的小事件循环运行。 For example, you can't create a "background" task whose execution spans several calls to asyncio.run() , and that kind of thing can be very useful for logging, monitoring, or timeout. 例如,您无法创建一个“后台”任务,其执行跨越多次调用asyncio.run() ,这种事情对于记录,监视或超时非常有用。 (Using asyncio.run could also be a performance issue because it creates a whole new event loop on every invocation, but this can be fixed by switching to run_until_complete .) (使用asyncio.run也可能是性能问题,因为它会在每次调用时创建一个全新的事件循环,但这可以通过切换到run_until_completerun_until_complete 。)

But there is also a third option: 但还有第三种选择:

  • Create a separate thread that only executes loop.run_forever() and waits to be given work to do. 创建一个单独的线程,只执行loop.run_forever()并等待给予工作。 The remainder of the program consists of normal blocking code that can request something from asyncio using asyncio.run_coroutine_threadsafe() . 程序的其余部分包含普通的阻塞代码,可以使用asyncio.run_coroutine_threadsafe()从asyncio请求内容。 That function doesn't block; 该功能不会阻止; it immediately returns a concurrent.futures.Future which you can pass around and whose result() method automatically waits for the result to be available. 它会立即返回一个concurrent.futures.Future ,你可以传递它并且其result()方法会自动等待结果可用。 It supports additional features, such as waiting for multiple instances to complete in parallel using wait , the as_completed iterator, etc. 它支持其他功能,例如等待使用wait并行完成多个实例, as_completed迭代器等。

This approach IMHO combines the best characteristics of the two options from the question. 这种方法恕我直言结合了问题中两个选项的最佳特征。 It leaves blocking code truly blocking, still being allowed to wait for things to happen, spawn threads etc., without forcing the use of async def and run_in_executor across the board. 它使阻塞代码真正阻塞,仍允许等待事情发生,产生线程等,而不必强制使用async defrun_in_executor At the same time the asyncio parts can be written using asyncio best practices, with a long-running event loop servicing the whole program. 同时,可以使用asyncio最佳实践编写asyncio部件,并为整个程序提供长时间运行的事件循环。 You just need to be careful for all interfacing with the event loop from the rest of the application (even to call something as simple as loop.stop ) to be done using loop.call_soon_threadsafe and asyncio.run_coroutine_threadsafe . 您只需要小心所有与应用程序其余部分的事件循环接口(甚至调用像loop.stop这样简单的东西),使用loop.call_soon_threadsafeasyncio.run_coroutine_threadsafe来完成。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM