Python中的异步上下文管理器

2025 年 3 月 7 日 | 阅读 22 分钟

在 Python 中,异步上下文管理器允许您在 async/await 场景中管理需要异步操作的对象。上下文管理器(with 语句)可以在同步上下文中创建和拆卸对象;异步上下文管理器(async with)扩展了这一概念,以非阻塞方式管理异步进程,例如执行 I/O 密集型任务、管理连接或处理锁。

什么是异步上下文管理器?

异步上下文管理器是 Python 中的一种特殊对象,它使用两个特定的方法

  • __aenter__() 和 __aexit__()。这些方法允许它以异步方式管理资源。

但在我们深入探讨异步上下文管理器的独特之处之前,让我们先回顾一下常规上下文管理器是什么。

什么是上下文管理器?

上下文管理器是一种有助于高效管理资源的对象。它有两个关键方法

  • __enter__(): 每当您使用 with 语句执行代码块时,都会调用此函数。它通常用于初始化任务,例如打开文件或连接到网络。
  • __exit__(): 每当您退出代码块时(无论是正常完成还是出现错误),都会调用此函数。它负责清理操作,例如关闭文件和释放资源。

上下文管理器如何工作?

1. 当您将上下文管理器与语句结合使用时,会发生以下情况。

  • 上下文管理器在包含块的开头建立,并立即调用 __enter__() 函数。这会创建资产。
  • 在块内,您可以使用资源。
  • 当对象成功或不成功退出时,__exit__() 函数会立即被调用以进行清理。

2. 上下文管理器可以用于许多活动,包括

  • 打开和关闭文件。
  • 管理网络连接。
  • 确保资源被正确锁定和解锁。

示例

这类似于 try-finally 表达式。

接下来,让我们看看异步上下文管理器。

异步上下文管理器

就像常规上下文管理器一样,异步上下文管理器也管理资源,但它们旨在与异步代码一起使用。这意味着它们有助于确保在处理可能需要时间完成的任务(例如下载数据或等待用户输入)时,资源得到妥善处理。

异步上下文管理器是在 PEP 492 中引入的,该 PEP 将 "async" 和 "await" 语法添加到 Python 中。

简单地说,异步上下文管理器允许您在进入和退出上下文后暂停并重新启动代码的执行。这在您需要执行异步操作(例如响应网络请求或读取文件)同时不干扰应用程序的其余功能的情况下非常有用。

当您创建异步上下文管理器时,您会添加两个独特的方法:__aenter__ 和 __aexit__。这些方法被构建为协程,这意味着它们可以暂停和重新启动。您可以使用 "async with" 声明来使用它们,从而使您的代码更具可读性和可管理性。

请务必记住,异步上下文管理器只能在实现 asyncio 库的应用程序中使用,尤其是在调用其他协程时。这意味着它们是专门为异步编程上下文设计的。

上下文管理器 vs 异步上下文管理器

在深入 Python 编程时,您会遇到经典和异步上下文管理器。虽然它们有不同的目的,但它们有一些共同的基本原则,可以使资源管理和异常处理更加优雅和高效。

上下文管理器的本质

经典和异步上下文管理器都旨在使文件句柄和网络连接等对象的控制更容易,并确保它们在每次使用后得到适当处理。它们通过模仿 try-finally 语句的工作方式来实现这一点,但使用更具可读性和易懂性的语法。

1. 经典上下文管理器: 经典上下文管理器是最常见的类型。它们依赖于两个基本方法

  • __enter__(): 此方法在上下文管理器管理的 C 块的开头调用。它设置必要的资源并返回您可能需要的任何值。
  • _exit_(): 此方法在退出代码块时调用,无论是因为正常退出还是由于异常。此方法处理清理工作,例如关闭文件或释放网络连接。您通常使用带有 with 语句的经典上下文管理器,如下所示
  • 在此示例中,文件被打开以供读取,当块退出时,即使在读取时发生错误,文件也会自动关闭。

2. 异步上下文管理器

另一方面,异步上下文管理器设计用于异步编程环境,特别是在使用 Python 的 asyncio 库时。它们采用 _aenter_ 和 _aexit_ 方法,这些方法专门用于处理异步操作。

  • _aenter(): 类似于 __enter_,此方法在进入异步块时调用。它准备资源以在异步上下文中使用。
  • _aexit_(): 此方法在退出异步块时调用,确保资源得到正确清理。
  • 要使用异步上下文管理器,您需要 async with 语句
  • 在这种情况下,aiofiles 是支持非阻塞文件操作的异步库的一个示例。在这里,async with 语句确保文件被打开,异步读取,并自动关闭,同时允许其他操作并发运行。

主要区别总结

  1. 使用范围
    1. 经典上下文管理器: 可以在 Python 程序的任何地方使用,提供一种直接的资源管理方式。
    2. 异步上下文管理器: 专门用于 asyncio 程序中,通常出现在协程中。
  2. 语法
    1. 经典: 实现 _enter() 和 __exit_() 方法,并与 with 一起使用。
    2. 异步: 实现 _aenter() 和 __aexit_() 方法,并与 async with 一起使用。
  3. 行为
    1. 经典: 处理阻塞操作。
    2. 异步: 处理非阻塞操作,允许并发执行。

了解经典和异步上下文管理器对于编写清晰、高效的 Python 代码至关重要。它们不仅使资源管理更容易,而且通过清晰地划分资源使用的开始和结束来增强代码可读性。有了这个基础,您现在可以探索利用这些上下文管理技术的更复杂场景和库。

如何使用异步上下文管理器?

Python 中的异步上下文管理器本质上是一个遵循特定协议的对象——它必须有两个关键方法:__aenter__() 和 __aexit__()。

这些上下文管理器之所以特殊,是因为这两个方法都必须写成协程,这意味着它们使用 async def 并返回可以被 await 的对象。简单来说,这些方法需要能够暂停并等待事情发生,这使得它们非常适合异步编程。

示例

输出

 
> entering the context manager
> inside the context manager
> exiting the context manager   

代码解释

此代码使用 asyncio 库定义了一个简单的 Python 异步上下文管理器。让我们分解一下

  • 类定义 (AsyncContextManager): AsyncContextManager 类是我们的自定义异步上下文管理器。这个类将异步管理资源,使其在涉及异步操作(如文件处理或网络连接)的场景中非常有用。
  • __aenter__() 方法: __aenter__() 方法在进入 async with 块时执行。它必须使用 async def 关键字定义,因为它是异步的。
  • 当调用此方法时,它只是打印
  • 您可以在此处添加其他功能,例如打开连接或分配您需要在上下文中管理的资源。
  • __aexit__() 方法: __aexit__() 方法在退出 async with 块时执行。与 __aenter__() 类似,它是异步的。此方法还可以处理上下文本块中引发的异常。此方法打印
  • 在实际应用程序中,它可用于清理资源,例如关闭文件或网络连接。

由于每种方法都是一个协程,因此它们可能会等待其他协程或任务。

示例

输出

 
> starting the async manager
> inside the async manager
> closing the async manager   

代码解释

此代码定义了一个名为 CustomAsyncManager 的异步上下文管理器。在 Python 中,上下文管理器允许您在需要时精确地分配和释放资源。异步上下文管理器使用 __aenter__ 和 __aexit__ 来处理异步操作,例如与 asyncio 配合使用以暂停执行而不阻塞其他任务。

  • 类定义 (CustomAsyncManager): 此类充当异步上下文管理器,使其能够在 async with 块中使用。
  • __aenter__ 方法: 当执行进入 async with 块时调用此方法。
  • 它打印消息 '>starting the async manager'。
  • 然后,它使用 await asyncio.sleep(0.5) 暂停执行 0.5 秒。await 关键字很重要,因为它确保事件循环可以在等待时运行其他任务,从而使上下文非阻塞。
  • __aexit__ 方法: 此方法在退出 async with 块时执行,无论它是正常结束还是由于异常。
  • 它打印消息 '>closing the async manager'。
  • 与 __aenter__ 类似,它使用 await asyncio.sleep(0.5) 暂停 0.5 秒。
  • __aexit__ 中的参数: exc_cls、exc_inst 和 traceback 是与异常处理相关的参数。如果 async with 块中发生错误,它们引用异常类型、实例和 traceback。如果没有发生异常,它们将为 None。

使用异步上下文管理器

异步上下文管理器通过 "async with" 表达式使用。

这将自动 await 进入和退出协程,根据需要暂停调用协程。

示例

手动使用异步上下文管理器的示例

让我们逐步看看如何手动使用异步上下文管理器。

  1. 首先,我们将创建一个异步上下文管理器。该管理器将有两个关键方法:一个用于启动(“进入”方法),另一个用于结束(“退出”方法)。这两个方法都将打印一条消息,让我们知道它们何时执行,并且它们还会暂停一小段时间(就像小睡一会儿)。这种暂停很有用,因为它模拟了需要时间才能完成的实际任务,例如等待数据从互联网加载。
    简而言之,上下文管理器会暂停,以便程序的其他部分可以在等待时继续工作。
  2. 现在,我们不是让 Python 像通常那样自动处理,而是手动调用“进入”和“退出”方法。这意味着我们需要从这些方法中获取任务(称为“awaitables”)并告诉程序等待它们完成。
    通过手动操作,我们可以清楚地看到幕后发生的一切。通常,您不需要自己这样做。相反,大多数人使用一种更简单的方法,称为 async with。但现在,我们将采取迂回的方式来更好地理解它。
  3. 当您运行示例时,程序首先创建 main() 协程,它充当 asyncio 程序的起点。
    在 main() 中,创建了 AsyncContextManager 类的一个实例。
  4. 下一步是调用 __aenter__() 协程,它返回一个可以被 await 的对象。
  5. 当 await 这个对象时,main() 协程会暂时暂停,允许 __aenter__() 协程运行。它打印一条消息并短暂暂停(使用 sleep)。
  6. 一旦 __aenter__() 完成,控制权返回到 main() 协程,它打印另一条消息。
  7. 之后,程序继续调用 __aexit__() 协程,它像以前一样,返回一个可以被 await 的东西。
  8. 再次 await 第二个对象会暂停 main(),让 __aexit__() 有机会运行。它打印自己的消息并短暂睡眠,然后完成。
  9. 最后,main() 协程再次恢复并结束程序。

尽管这个例子可能有些牵强,但它演示了当您使用带有 "async with" 语句的上下文管理器时幕后发生的情况——您将在下一节中看到。

这是展示其工作原理的完整示例

示例

输出

 
> starting the context manager
doing work within the manager
> ending the context manager   

代码解释

此 Python 代码演示了如何使用 asyncio 模块手动使用异步上下文管理器。以下是每个部分的分解

  • 类定义 MyAsyncManager: 类 MyAsyncManager 是一个异步上下文管理器。在 Python 中,通过实现两个特殊方法来定义异步上下文管理器
    • __aenter__()(用于进入上下文)和 __aexit__()(用于退出上下文)。这些是常规上下文管理器中 __enter__() 和 __exit__() 的异步等效项。
  • __aenter__(): 在进入上下文管理器时调用此方法(例如使用 with 语句)。在此代码中
    • 它打印消息 ">starting the context manager"。
    • 它使用 await asyncio.sleep(0.5) 模拟 0.5 秒的延迟。
  • __aexit__(): 在退出上下文管理器时调用此方法。此处
    • 它打印消息 ">ending the context manager"。
    • 它还模拟 0.5 秒的延迟。
  • 协程 task(): 这是一个简单的协程(使用 await 或 async 的函数),它手动管理异步上下文。
  • 创建管理器实例: 在协程内部,使用 manager_instance = MyAsyncManager() 创建 MyAsyncManager 的一个实例。
  • 手动进入上下文: 此代码没有使用 async with(使用异步上下文管理器的标准方式),而是手动执行此过程。
  • start_task = manager_instance.__aenter__() 获取 __aenter__() 的可等待结果,然后使用 await start_task 等待。这会进入上下文管理器并执行 __aenter__() 方法。
  • 在管理器中执行某些任务: print(f'doing work within the manager') 行表示在上下文管理器中正在进行某些工作。
  • 手动退出上下文: __aexit__() 方法也以类似的方式手动处理。
  • end_task = manager_instance.__aexit__(None, None, None) 获取用于退出管理器的可等待对象。这使用 await end_task 等待。
  • 处理事件循环: 由于 asyncio 函数需要事件循环才能运行,因此代码会检查是否已存在正在运行的循环。
  • if asyncio.get_running_loop(): 这会检查事件循环是否已激活。
  • 如果循环正在运行,代码使用 await task() 直接在循环中运行协程。
  • except RuntimeError: 如果没有活动的事件循环,则会引发 RuntimeError,并改为使用 asyncio.run(task()) 来创建新的事件循环并运行任务。

此代码演示了一个异步上下文管理器,它在不使用 async with 语法的情况下手动管理。

它会检查事件循环是否正在运行,然后决定如何运行异步代码(await 与 asyncio.run())。

异步上下文管理器和 "async with" 的示例。

我们可以学习如何利用 "async with" 语句来使用异步上下文管理器。

在以下示例中,我们将修改早期的方法,以演示如何以更典型的方式使用上下文管理器。

  1. 通过使用 "async with",我们可以在一行中创建并进入上下文管理器。这不仅简化了过程,而且还确保程序自动为我们等待 _aenter_ 方法。
  2. 进入内部块后,我们可以利用上下文管理器。例如,在这里,我们将简单地显示一条消息。
  3. 当我们退出块时,程序将自动等待 _aexit_ 方法,负责处理退出过程。
  4. 如果我们将此示例与上一个示例进行比较,很容易看出 "async with" 表达式在 asyncio 程序中为我们处理了多少工作。
  5. 此版本保持清晰,同时结合了更多的句子变化和复杂性,使其阅读起来更具吸引力且更自然。
  6. 当您运行示例时,它首先启动 main() 协程,它作为整个 asyncio 程序的入口点。
  7. 在 main() 中,协程通过创建 AsyncContextManager 类的一个实例来启动。这发生在 "async with" 语句中,这是一种在异步编程中使用的特殊类型的上下文管理。
  8. 这个 "async with" 语句的妙处在于它会自动调用 __aenter__ 方法(进入上下文的异步版本)。它等待协程完成其工作,这涉及打印一条消息并短暂暂停。
  9. 一旦暂停结束,main() 协程恢复并执行在上下文管理器主体中定义的任何工作。在这种情况下,它只是打印另一条消息。
  10. 完成该任务后,程序到达块的末尾并自动调用 __aexit__ 方法(上下文管理器的退出过程)。它再次等待,报告另一条消息,然后短暂暂停,然后继续。

此示例显示了在基于 asyncio 的程序中使用异步上下文管理器的典型方式。它效率高,自动处理流程,并确保一切都按明确定义的顺序发生——非常适合管理网络连接或文件等异步资源。

这是示例

示例

输出

 
> Entered the async manager
Inside the context manager
> Exiting the async manager   

代码解释

此代码演示了在 Python 中使用异步上下文管理器,利用 async 和 await 通过 __aenter__ 和 __aexit__ 方法管理异步资源。

  • 这是一个自定义异步上下文管理器类,它定义了 __aenter__ 和 __aexit__,这些是上下文管理中使用的特殊方法。
  • async def __aenter__(self): 此方法在进入 async with 块时执行。
  • 它打印 ">Entered the async manager",然后等待 asyncio.sleep(0.5) 以模拟一些异步工作或延迟(例如,获取资源)。
  • async def __aexit__(self, exc_type, exc_value, traceback): 此方法在退出 async with 块时执行,无论是否发生异常。
  • 它打印 ">Exiting the async manager",然后使用 asyncio.sleep 再次延迟 0.5 秒。
  • run_async_task: 这是一个协程,演示了如何使用异步上下文管理器。
  • 在函数内部,async with 语句用于创建上下文,在该上下文中执行 MyAsyncManager 的异步方法。
  • 使用 async with MyAsyncManager() 时: 调用 __aenter__ 方法,打印 ">Entered the async manager",然后等待 0.5 秒。
  • 之后,执行 print('Inside the context manager') 语句。
  • 当上下文管理器块结束时(在 print 语句之后),调用 __aexit__ 方法,打印 ">Exiting the async manager",然后再次延迟 0.5 秒。
  • await run_async_task(): 此行用于在事件循环中调用 run_async_task() 协程。
  • d
  • await 关键字确保 run_async_task 中的异步任务在现有事件循环(例如 Jupyter 的事件循环)中异步运行。

带异常的异步上下文管理器示例

要了解在异步上下文管理器内部引发异常时会发生什么,让我们通过一个示例来了解。

  1. 我们将采用与之前相同的异步上下文管理器,它与 async with 表达式一起使用,并在内部代码块中引入一个异常。
  2. 此示例的目的是演示,无论内部块如何退出(无论是正常退出还是由于错误),async with 表达式都确保上下文管理器的退出过程得到正确等待和执行。
  3. 运行此示例时,程序首先创建 main() 协程,它作为异步 asyncio 程序的起点。
  4. 在 main() 中,我们使用 "async with" 语句创建了一个名为 AsyncContextManager 的类的实例。这会触发 __aenter__ 方法(类似于常规上下文管理器中的 __enter__),程序等待协程完成。此时,打印一条消息,程序短暂暂停。
  5. 之后,main() 协程继续执行。它运行上下文管理器的主要部分,打印另一条消息,然后故意抛出异常。
  6. 由于异常,程序会自动移出上下文管理器块,调用 __aexit__ 方法。此方法被等待,打印另一条消息,程序短暂睡眠,然后继续。
  7. 最后,异常导致程序的事件循环停止。

这是说明这一点的完整示例

示例

输出

 
> Entering the context handler
Inside the handler
> Exiting the context handler
Caught an exception: An error occurred   

代码解释

此 Python 代码演示了如何使用 async with 语法实现异步上下文处理器。它还展示了如何在协程中使用此处理器以及如何处理在 async with 块中引发的异常。

  • MyAsyncContextHandler: 这是一个异步上下文管理器,提供 __aenter__ 和 __aexit__ 方法。这些是标准 __enter__ 和 __exit__ 方法(在同步上下文管理器中使用)的异步等效项。
  • async def __aenter__(self): 当代码进入 async with 块时调用此方法。
  • 它打印 ">Entering the context handler",然后等待 0.5 秒,使用 await asyncio.sleep(0.5) 模拟异步操作。
  • async def __aexit__(self, exc_type, exc_value, traceback): 当代码退出 async with 块时调用此方法。
  • 它打印 ">Exiting the context handler",然后等待另外 0.5 秒,以模拟异步清理操作。
  • 它还接受捕获 async with 块中引发的任何异常的参数(exc_type、exc_value 和 traceback)。
  • 函数:sample_coroutine
    • 这是一个使用 MyAsyncContextHandler 的异步协程。
    • 在此协程中:async with MyAsyncContextHandler() as handler
    • 此行进入上下文管理器,调用 __aenter__ 方法。
    • 进入上下文后,打印消息 "Inside the handler"。
    • raise Exception('An error occurred') 故意引发异常。这将导致控制流转到 __aexit__ 方法以处理清理。
  • 异常处理
    • 协程 sample_coroutine() 在 try...except 块中等待。当 async with 块中引发异常时,它会在上下文管理器退出后被 except 块捕获。
    • 消息 "Caught an exception: An error occurred" 在捕获到异常时打印。
  • 执行流程
    • 协程进入 async with 块,打印 ">Entering the context handler",然后延迟 0.5 秒。
    • 在块内,打印 "Inside the handler"。
    • 引发异常,消息为 "An error occurred"。
    • 调用 __aexit__ 方法退出上下文。它打印 ">Exiting the context handler",然后等待另外 0.5 秒。
    • 退出后,try...except 块中捕获到引发的异常,并打印消息 "Caught an exception: An error occurred"。

开始使用异步上下文管理器通常会导致错误。

在本节中,我们将介绍人们在使用异步上下文管理器时遇到的一些常见错误以及如何修复它们。

异步上下文管理器和 "with" 表达式的示例

为了分解尝试错误使用异步上下文管理器时会发生什么,让我们逐步探讨情况。

想象一下,您正在使用异步上下文管理器与常规的 with 表达式,而不是正确的 async with 表达式。在这种情况下,您仍然尝试异步管理资源,但您没有使用正确的语法。

当您尝试此操作时,代码将遇到错误。为什么?因为您正在使用的异步上下文管理器不遵循常规上下文管理器的规则,特别是它缺少正常上下文管理器使用的 __enter__() 等方法。因此,当 with 表达式使用不正确时,它将不知道如何处理事情,从而导致抛出异常。

简而言之,如果您在处理异步上下文管理器时不使用 async with,代码将无法正常工作,并且您将收到错误。以下是这种情况以代码形式发生的完整示例。

示例

输出

 
> Entering the custom async manager
Inside the custom manager
> Exiting the custom async manager
<ipython-input-5-1b59ca19a9b6>:33: RuntimeWarning: coroutine 'sample_coroutine' was never awaited
  await sample_coroutine()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback   

代码解释

此代码演示了在 Python 中使用协程的异步上下文管理器,同时处理不同的运行时环境(例如普通 Python 脚本与具有已运行事件循环的环境,如 Jupyter notebook)。以下是每个部分的解释

  • 导入 asyncio: asyncio 模块提供了使用协程编写单线程并发代码的基础结构,使其适用于 I/O 密集型和高级结构化网络代码。
  • 定义异步上下文管理器: MyAsyncManager 类是一个异步上下文管理器。异步上下文管理器类似于常规上下文管理器(例如,与 with 语句一起使用),但它支持异步操作。
  • 进入异步上下文管理器时调用 __aenter__() 方法。它打印一条消息并使用 await asyncio.sleep(0.5) 模拟延迟。
  • 退出上下文管理器时调用 __aexit__() 方法,也打印一条消息并模拟延迟。
  • 定义协程函数: sample_coroutine() 是一个异步协程。
  • 它使用带有 async with 语法的异步上下文管理器 (MyAsyncManager),这确保了 __aenter__ 和 __aexit__ 方法都被异步调用。
  • 在上下文管理器内部,打印一条消息: "Inside the custom manager"。这模拟了使用上下文管理器提供的资源。
  • 处理不同的运行时环境
  • if __name__ == "__main__": 是一种常见的 Python 惯用法,用于确保特定代码仅在直接执行脚本时运行(而不是作为模块导入时)。
  • asyncio.run() 函数通常用于从主入口点运行 asyncio 程序。它启动事件循环并运行协程。
  • 错误处理: try-except 块用于捕获 RuntimeError。当从已运行事件循环的环境(例如 Jupyter notebook)调用 asyncio.run() 时,会发生此错误。
  • 如果发生 RuntimeError,代码会直接使用 await sample_coroutine()。此方法避免在事件循环已激活时调用 asyncio.run()。

没有 awaitables 的异步上下文管理器示例

让我们仔细看看,如果我们尝试使用异步上下文管理器,但使用常规函数而不是协程来定义它,会发生什么。

在这种情况下,我们仍将像往常一样使用异步上下文管理器。主要区别在于我们如何定义类:我们不会将 __enter__ 和 __exit__ 方法设置为协程(可以被等待),而是使用常规的 Python 函数。

问题是,当您使用 async with 时,Python 期望一些被称为 awaitables 的东西。这些是 Python 可以暂停和等待的对象,就像协程一样。然而,当我们用普通函数替换协程时,我们不会返回可以被等待的东西。例如,一个普通函数可能只返回 None——这会使 async with 块出现问题,因为它正在等待它可以“等待”的东西。

结果,这种不匹配会导致错误。

这是演示此操作的完整示例。

示例

输出

 
> Entering the context handler
Inside the handler
> Exiting the context    

代码解释

让我们一步一步地分解代码,以了解其工作原理,特别是异步上下文管理器的实现。

  • 导入: 此行导入 asyncio 模块,该模块提供使用 async/await 语法编写并发代码的支持。
  • 定义异步上下文处理器: AsyncHandler 类被定义为异步上下文管理器。
  • 进入上下文时调用 __aenter__ 方法(在 async with 语句内部)
  • 它打印一条消息,指示进入上下文。
  • 它返回 self,允许上下文管理器在 async with 块中被引用为 handler。
  • 退出上下文时调用 __aexit__ 方法
  • 它打印一条消息,指示退出上下文。
  • 它还可以处理上下文中发生的异常(尽管此示例未显示该功能)。
  • 定义协程: my_coroutine 函数被定义为异步协程。
  • 在此协程内部,创建了 AsyncHandler 的一个实例并在 async with 块中使用。
  • 进入块时,调用 __aenter__,执行 print 语句。
  • 块内的代码运行,打印 "Inside the handler。"
  • 退出块时,调用 __aexit__,执行退出消息。
  • 运行协程: main 协程被定义为调用 my_coroutine。这充当运行异步操作的入口点。
  • 处理事件循环: 代码使用 asyncio.get_running_loop() 检查是否有当前正在运行的事件循环。
  • 如果事件循环正在运行,它会调用 await main(),在现有循环中执行 main 协程。
  • 如果没有循环正在运行(这会引发 RuntimeError),它会使用 asyncio.run(main()) 启动一个新的事件循环并运行 main 协程。

结论

Python 中的异步上下文管理器通过允许资源以非阻塞方式设置和释放,从而在异步编程中实现高效的资源管理。它们利用 async with 语句,通过与 asyncio 任务良好集成,确保更流畅的并发性,从而在复杂的 I/O 密集型应用程序中产生更具可读性和性能的代码。