【问题标题】:When do I need to use an executor for async code? What happens when I don't (but should)?我什么时候需要为异步代码使用执行器?当我不(但应该)时会发生什么?
【发布时间】:2020-08-23 10:53:30
【问题描述】:

asyncio docs 明确表示 asyncio 代码不应直接调用阻塞代码,还指定了使用异步代码运行阻塞代码的方式:

不应直接调用阻塞(CPU 绑定)代码。例如,如果一个函数执行 1 秒的 CPU 密集型计算,则所有并发的 asyncio 任务和 IO 操作都会延迟 1 秒。

执行器可用于在不同线程甚至不同进程中运行任务,以避免事件循环阻塞 OS 线程。

然而,这个描述对于应该在什么时候使用执行器并不是很具体。很明显,“1 秒的 CPU 密集型计算”会是个问题,但 0.1 秒会是个问题吗?还是 0.01s?

文档还提供了example

def cpu_bound():
    return sum(i * i for i in range(10 ** 7))

作为在执行器中运行的东西(在不到一秒的时间内运行)。

(尽管他们可能会将此作为使用线程与进程的示例,但它仍然是我的意思的示例——如果它是range(10 ** 6) 等,我会在执行程序中运行它吗?)


this的回答中,声明

大部分标准库由常规的“阻塞”函数和类定义组成。他们的工作很快,所以即使他们“阻止”,他们也会在合理的时间内返回。

...

标准库函数和方法的加载速度很快,为什么要在单独的线程中运行 str.splitlines()urllib.parse.quote() 而只执行代码并完成它会快得多?

但是什么才算“合理时间”呢?我什么时候可以“只执行代码并完成它”?


我的问题是:

  1. 您如何确定需要执行人?
  2. 如果您的代码“阻塞”时间过长,实际会发生什么?有什么迹象表明是这种情况?

【问题讨论】:

  • 合理的时间意味着它不会阻止其他线程运行(需要这样做)的时间不会太长。一次只运行一个线程,因为它们通常不会在 Python 中同时运行。当您超出限制时,代码将停止执​​行您想要完成的任何操作 - 没有硬性规定。
  • @martineau Stephen 的问题是关于 asyncio,它是真正的单线程,而不仅仅是由 GIL 序列化。正如您所指出的,多线程没有并行性 - 但您至少可以依赖 Python 偶尔在线程之间切换。持有 GIL 的线程要么遇到阻塞函数,立即释放 GIL,要么在15 milliseconds 之后释放。 Asyncio 是合作的,并且不提供这样的承诺:无需等待任何东西即可运行的代码可能会无限期地阻塞所有其他 asyncio 任务。

标签: python python-3.x python-asyncio


【解决方案1】:

你如何确定需要一个执行者?

这个问题并不是 asyncio 独有的。据我所知,还没有人提出一个精确的标准。

当前的做法与其他与性能相关的决策相同:通过结合常识和分析来确定。常识会告诉您urllib.parse.quote() 可以在事件循环线程中调用,但使用 BeautifulSoup 解析任意大小的 HTML 文档可能不行。根据经验,协程可以包含您愿意在 Twisted 等经典异步系统的回调中放置的那种代码。

如果您的代码“阻塞”时间过长,实际会发生什么?有什么迹象表明是这种情况?

您会注意到延迟增加和吞吐量下降。

程序的预期延迟可能是决定何时开始使用执行程序的因素。另请注意,将某些东西交给执行者有其自身不可忽略的开销,因此您不想对所有事情都这样做,如果您为非常快的事情(例如代码)这样​​做,它实际上会减慢速度归结为几个 dict 查找)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-28
    • 2015-04-05
    • 2012-06-28
    • 1970-01-01
    • 2022-01-07
    • 2012-09-22
    相关资源
    最近更新 更多