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 事件循环调用,则会引发错误。默认修补的方法包括 os
、io
、time
、socket
、sqlite
模块的方法。有关 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中文网其他相关文章!

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

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

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

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

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

要将列表连接成字符串,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

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


热AI工具

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

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

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

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

热门文章

热工具

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

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

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

EditPlus 中文破解版
体积小,语法高亮,不支持代码提示功能

SublimeText3汉化版
中文版,非常好用