搜索
首页后端开发Python教程BlockBuster 简介:我的异步事件循环被阻止了吗?

Introducing BlockBuster: is my asyncio event loop blocked?

Python 3.5 引入了异步 I/O 作为线程的替代方案来处理并发。异步 I/O 和 Python 中的 asyncio 实现的优势在于,通过不产生内存消耗大的操作系统线程,系统使用更少的资源并且更具可扩展性。此外,在 asyncio 中,调度点通过 await 语法明确定义,而在基于线程的并发中,GIL 可能会在难以预测的代码点释放。因此,基于 asyncio 的并发系统更容易理解和调试。最终,可以取消 asyncio 任务,而这在使用线程时不容易做到。

但是,为了真正受益于这些优势,在异步协程中避免阻塞调用非常重要。阻塞调用可以是网络调用、文件系统调用、sleep 调用等等。这些阻塞调用是有害的,因为在底层,asyncio 使用单线程事件循环来并发运行协程。因此,如果在协程中进行阻塞调用,它会阻塞整个事件循环和所有协程,从而影响应用程序的整体性能。

以下是一个阻塞调用阻止代码并发执行的示例:

import asyncio
import datetime
import time

async def example(name):
    print(f"{datetime.datetime.now()}: {name} start")
    time.sleep(1)  # time.sleep 是一个阻塞函数
    print(f"{datetime.datetime.now()}: {name} stop")

async def main():
    await asyncio.gather(example("1"), example("2"))

asyncio.run(main())

运行结果类似于:

<code>2025-01-07 18:50:15.327677: 1 start
2025-01-07 18:50:16.328330: 1 stop
2025-01-07 18:50:16.328404: 2 start
2025-01-07 18:50:17.333159: 2 stop</code>

可以看到,两个协程没有并发运行。

为了克服这个问题,你需要使用非阻塞等效项或将执行推迟到线程池:

import asyncio
import datetime
import time

async def example(name):
    print(f"{datetime.datetime.now()}: {name} start")
    await asyncio.sleep(1)  # 将阻塞的 time.sleep 调用替换为非阻塞的 asyncio.sleep 协程
    print(f"{datetime.datetime.now()}: {name} stop")

async def main():
    await asyncio.gather(example("1"), example("2"))

asyncio.run(main())

运行结果类似于:

<code>2025-01-07 18:53:53.579738: 1 start
2025-01-07 18:53:53.579797: 2 start
2025-01-07 18:53:54.580463: 1 stop
2025-01-07 18:53:54.580572: 2 stop</code>

这里两个协程并发运行。

现在的问题是,并不总是很容易识别一个方法是否阻塞。特别是如果代码库很大或使用第三方库。有时,阻塞调用是在代码的深层部分进行的。

例如,这段代码是否阻塞?

import blockbuster
from importlib.metadata import version

async def get_version():
    return version("blockbuster")

Python 是否在启动时将包元数据加载到内存中?是在加载 blockbuster 模块时完成的吗?或者在我们调用 version() 时?结果是否被缓存,后续调用将是非阻塞的吗?正确答案是在调用 version() 时完成的,它涉及读取已安装包的 METADATA 文件。并且结果没有被缓存。因此,version() 是一个阻塞调用,应该始终推迟到线程中。如果不深入研究 importlib 的代码,很难知道这个事实。

检测阻塞调用的一种方法是激活 asyncio 的调试模式来记录耗时过长的阻塞调用。但这并不是最有效的方法,因为许多短于触发超时时间的阻塞仍然会损害性能,并且测试/开发中的阻塞时间可能与生产环境中的不同。例如,如果数据库必须获取大量数据,则数据库调用在生产环境中可能需要更长时间。

这就是 BlockBuster 发挥作用的地方!激活后,BlockBuster 将修补几个阻塞的 Python 框架方法,如果它们从 asyncio 事件循环调用,则会引发错误。默认修补的方法包括 osiotimesocketsqlite 模块的方法。有关 BlockBuster 检测到的方法的完整列表,请参阅项目自述文件。然后,你可以在单元测试或开发模式中激活 BlockBuster 来捕获任何阻塞调用并修复它们。如果你知道 JVM 中很棒的 BlockHound 库,它的原理相同,但适用于 Python。BlockHound 是 BlockBuster 的一个很好的灵感来源,感谢创建者。

让我们看看如何在上面阻塞代码片段上使用 BlockBuster。

首先,我们需要安装 blockbuster

import asyncio
import datetime
import time

async def example(name):
    print(f"{datetime.datetime.now()}: {name} start")
    time.sleep(1)  # time.sleep 是一个阻塞函数
    print(f"{datetime.datetime.now()}: {name} stop")

async def main():
    await asyncio.gather(example("1"), example("2"))

asyncio.run(main())

然后,我们可以使用 pytest fixture 和 blockbuster_ctx() 方法来在每个测试开始时激活 BlockBuster,并在拆卸期间停用它。

<code>2025-01-07 18:50:15.327677: 1 start
2025-01-07 18:50:16.328330: 1 stop
2025-01-07 18:50:16.328404: 2 start
2025-01-07 18:50:17.333159: 2 stop</code>

如果你用 pytest 运行这个,你会得到

import asyncio
import datetime
import time

async def example(name):
    print(f"{datetime.datetime.now()}: {name} start")
    await asyncio.sleep(1)  # 将阻塞的 time.sleep 调用替换为非阻塞的 asyncio.sleep 协程
    print(f"{datetime.datetime.now()}: {name} stop")

async def main():
    await asyncio.gather(example("1"), example("2"))

asyncio.run(main())

注意: 通常,在一个真实的项目中,blockbuster() fixture 将在一个 conftest.py 文件中设置。

结论

我相信 BlockBuster 在 asyncio 项目中非常有用。它已经帮助我在我参与的项目中检测到许多阻塞调用问题。但这并不是灵丹妙药。特别是,一些第三方库不使用 Python 框架方法来与网络或文件系统交互,而是包装 C 库。对于这些库,可以在测试设置中添加规则来触发这些库的阻塞调用。BlockBuster 也是开源的:非常欢迎贡献,以便在核心项目中为你的最喜欢的库添加规则。如果你看到问题和可以改进的地方,我很乐意在项目问题跟踪器中收到你的反馈。

一些链接:

  • GitHub 项目
  • 问题
  • Pypi 上的包

以上是BlockBuster 简介:我的异步事件循环被阻止了吗?的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
Python的执行模型:编译,解释还是两者?Python的执行模型:编译,解释还是两者?May 10, 2025 am 12:04 AM

pythonisbothCompileDIntered。

Python是按线执行的吗?Python是按线执行的吗?May 10, 2025 am 12:03 AM

Python不是严格的逐行执行,而是基于解释器的机制进行优化和条件执行。解释器将代码转换为字节码,由PVM执行,可能会预编译常量表达式或优化循环。理解这些机制有助于优化代码和提高效率。

python中两个列表的串联替代方案是什么?python中两个列表的串联替代方案是什么?May 09, 2025 am 12:16 AM

可以使用多种方法在Python中连接两个列表:1.使用 操作符,简单但在大列表中效率低;2.使用extend方法,效率高但会修改原列表;3.使用 =操作符,兼具效率和可读性;4.使用itertools.chain函数,内存效率高但需额外导入;5.使用列表解析,优雅但可能过于复杂。选择方法应根据代码上下文和需求。

Python:合并两个列表的有效方法Python:合并两个列表的有效方法May 09, 2025 am 12:15 AM

有多种方法可以合并Python列表:1.使用 操作符,简单但对大列表不内存高效;2.使用extend方法,内存高效但会修改原列表;3.使用itertools.chain,适用于大数据集;4.使用*操作符,一行代码合并小到中型列表;5.使用numpy.concatenate,适用于大数据集和性能要求高的场景;6.使用append方法,适用于小列表但效率低。选择方法时需考虑列表大小和应用场景。

编译的与解释的语言:优点和缺点编译的与解释的语言:优点和缺点May 09, 2025 am 12:06 AM

CompiledLanguagesOffersPeedAndSecurity,而interneterpretledlanguages provideeaseafuseanDoctability.1)commiledlanguageslikec arefasterandSecureButhOnderDevevelmendeclementCyclesclesclesclesclesclesclesclesclesclesclesclesclesclesclesclesclesclesandentency.2)cransportedeplatectentysenty

Python:对于循环,最完整的指南Python:对于循环,最完整的指南May 09, 2025 am 12:05 AM

Python中,for循环用于遍历可迭代对象,while循环用于条件满足时重复执行操作。1)for循环示例:遍历列表并打印元素。2)while循环示例:猜数字游戏,直到猜对为止。掌握循环原理和优化技巧可提高代码效率和可靠性。

python concatenate列表到一个字符串中python concatenate列表到一个字符串中May 09, 2025 am 12:02 AM

要将列表连接成字符串,Python中使用join()方法是最佳选择。1)使用join()方法将列表元素连接成字符串,如''.join(my_list)。2)对于包含数字的列表,先用map(str,numbers)转换为字符串再连接。3)可以使用生成器表达式进行复杂格式化,如','.join(f'({fruit})'forfruitinfruits)。4)处理混合数据类型时,使用map(str,mixed_list)确保所有元素可转换为字符串。5)对于大型列表,使用''.join(large_li

Python的混合方法:编译和解释合并Python的混合方法:编译和解释合并May 08, 2025 am 12:16 AM

pythonuseshybridapprace,ComminingCompilationTobyTecoDeAndInterpretation.1)codeiscompiledtoplatform-Indepententbybytecode.2)bytecodeisisterpretedbybythepbybythepythonvirtualmachine,增强效率和通用性。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

安全考试浏览器

安全考试浏览器

Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用