首页 > 解决方案 > 是否有任何 linter 可以检测异步函数中的阻塞调用?

问题描述

https://www.aeracode.org/2018/02/19/python-async-simplified/

如果你调用一个非阻塞同步函数,它不会毁了你的一天,就像这样:

def get_chat_id(name):
    return "chat-%s" % name

async def main():
    result = get_chat_id("django")

然而,如果你调用一个阻塞函数,比如 Django ORM,异步函数中的代码看起来是一样的,但现在它是危险的代码,它可能会阻塞整个事件循环,因为它没有等待:

def get_chat_id(name):
    return Chat.objects.get(name=name).id

async def main():
    result = get_chat_id("django")

您可以看到,如果程序员对调用它的所有内容都没有超级了解,那么拥有一个“意外”变成阻塞的非阻塞函数是多么容易。这就是为什么我建议你永远不要在不安全的情况下从异步函数调用任何同步的东西,或者事先不知道它是一个非阻塞标准库函数,比如os.path.join.

所以我正在寻找一种方法来自动捕获这个错误的实例。是否有任何用于 Python 的 linter 会将异步函数中的同步函数调用报告为违规?

我可以配置 Pylint 或 Flake8 来执行此操作吗?

我不一定介意它是否也捕获了上面的第一种情况(这是无害的)。


更新:

正如米哈伊尔的回答所指出的那样,在某种程度上,我意识到这是一个愚蠢的问题。我们需要的是定义 linter 应该检测到的“危险同步函数”。

因此,出于这个问题的目的,我给出以下定义:

“危险的同步函数”是一种执行 IO 操作的函数。例如,这些操作必须由 gevent 进行猴子修补,或者必须包装在async函数中,以便事件循环可以上下文切换。

(我欢迎对此定义进行任何改进)

标签: python-3.xpython-asynciopylintflake8

解决方案


所以我正在寻找一种方法来自动捕获这个错误的实例。

让我们澄清一些事情:文章中讨论的错误是当您在某个 asyncio 协程中调用任何长时间运行的同步函数时(它可以是 I/O 阻塞调用或只是具有大量计算的纯 CPU 函数)。这是一个错误,因为它会阻止整个事件循环,这将导致性能显着下降(更多关于它的信息,包括答案下面的评论)。

有没有办法自动捕捉这种情况?在运行时间之前 - 不,没有人可以预测特定功能是否需要 10 秒或 0.01 秒才能执行。在运行时它已经内置了 asyncio,您所要做的就是启用调试模式。

如果您担心某些同步功能会在长时间运行(在调试模式下的运行时可检测到)和短时运行(不可检测)之间变化,只需使用run_in_executor在后台线程中执行函数- 它会保证事件循环不会被阻塞。


推荐阅读