Python 异步编程 - asyncio 和 await

2024年8月29日 | 阅读 7 分钟

asyncio 模块。asyncio 模块具有出色的功能,可让我们编写更高效的 Python 异步应用程序。我们将探讨如何在 Python 中管理异步事件循环。在深入研究此主题之前,让我们先了解什么是异步编程。

什么是异步编程?

同步编程中,方法被编写为一次执行一项任务。如果一个函数依赖于另一个函数的输出,则它必须等待该函数执行完成。程序实际上会一直停止,直到函数完成执行。这意味着一次只能执行一个程序。

这会减慢程序速度,因为它被迫停止并等待某项操作完成。系统中有很多处理器可用,因此将其他任务用于执行非理想任务是一种资源浪费。

为了克服这一点,异步编程概念发挥了作用。它的行为方式不同;它也一次执行一项。但是系统可能不会等待执行完成就进入下一步。

这意味着如果程序将在前一个程序尚未完成且仍在其他地方运行时执行另一项任务,则处理器就不会空闲。

在本教程中,我们将解释为什么我们需要这种类型的编程。

什么是 asyncio - 异步 I/O?

asyncio 是一个 Python 库,用于使用async/wait 运行并发代码。它是 Python 异步框架的基础,该框架提供连接库、网络和 Web 服务器、数据库分布式任务队列、高性能等。

此模块提供了围绕事件循环工作的框架,并负责 I/O 和系统事件等事物。

协程和任务

asyncio 是一个 Python 库,用于使用async/wait 运行并发代码。它是 Python 异步框架的基础,该框架提供连接库、网络、Web 服务器、数据库分布式任务队列、高性能等。

此模块提供了围绕事件循环工作的框架,并负责 I/O 和系统事件等事物。

示例 - 1

输出

Waiting 5 seconds. 
Hello
Hello
Hello
Hello
Hello
Finished waiting.

解释 -

在上面的代码中 -

  • 我们已导入 asyncio 模块以访问 Python 的async 功能。
  • 然后创建一个primary()函数,并在其前面写上 async 关键字。这将允许程序异步运行任务。
  • 我们使用了 for 循环并调用了sleep()方法,该方法强制我们等待 1 秒。
  • 程序在一秒钟后打印“Hello”。
  • 程序应该有一个.run()函数和一个.main()函数。

我们还可以调度绑定协程的任务或对象,并帮助它们运行。让我们了解以下示例。

示例 - 2

输出

started at 11:11:54
hello
world
finished at 11:11:57

解释 -

在上面的代码中,

  • 我们导入了 asyncio 和 time 模块。
  • 然后我们定义了带有 delay 和 value 参数的execute()函数。它使用sleep()方法打印延迟时间。
  • main()函数中,我们传递了两个参数:第一个是延迟时间,第二个是要打印的值。
  • 程序开始执行,打印确切的执行时间,然后打印“hello”,等待两秒钟,然后打印“world”并停止。

现在,让我们对上面的代码做一些更改,看看结果。

示例 - 3 创建任务

输出

started at 15:43:30
hello
world
finished at 15:43:32

我们可以看到它比上一个程序快 1 秒。create.task()方法将在事件循环中运行,其结果将放入任务中。我们调度了两个任务,并通过 await 返回了它们。

管理 Python 中的异步事件循环

Asyncio 也用于管理异步事件循环。事件循环是一个运行异步函数和回调的对象。当我们想要执行协程时,事件对于异步函数至关重要,当我们运行asyncio.run()方法时;事件循环对象会自动创建。要实现更高级的服务器,我们将需要对事件循环进行低级访问。我们需要直接处理事件循环的内部。

事件循环具有以下功能。

  • 它可以注册、执行和取消延迟调用(异步函数)
  • 它可以创建客户端和服务器传输以进行通信
  • 它可以创建子进程和传输以与其他程序进行通信。
  • 将函数调用委托给线程池。

让我们看下面的例子。

示例 -

输出

This is a asynchronicity!

事件循环通过获取asyncio.get_event_loop(),调度和运行异步任务,并在完成后关闭事件循环来启动。

在 Python 中使用 Stream 读写数据

asyncio 模块提供了stream,用于执行高级网络 I/O。它可以作为网络请求的服务器。它最适合长时间运行的网络操作,在这些操作中,应用程序会因为等待其他资源返回结果而阻塞。

asyncio 有两个类StreamReader 和 StreamWriter。这些类用于在高级级别读写网络数据。

要从网络读取,我们需要使用asyncio.open_connection()打开网络。StreamReaderStreamWriter对象函数返回一个元组,我们将对每个连接使用 .read() 和 .write() 方法。

asyncio.start_server() 方法用于接收来自远程主机的连接。此函数将回调函数client_connected_cb作为参数。每当函数收到请求时都会调用它。

同步 Python 中的任务

我们之前讨论过,异步程序是独立运行的,但有时我们希望它们之间进行通信。asyncio 模块为我们提供了队列和各种其他方法来建立任务之间的同步。

让我们了解以下实现方法。

  • 队列 - asyncio 队列促进异步函数排队 Python 对象,供其他异步函数使用。例如 - 工作负载根据其行为在函数之间分发。
  • 同步原语 - asyncio 的锁、事件、条件和信号量等功能可作为传统的 Python 对应项。

这里,有一点应始终牢记,这些方法不是线程安全的。对于在同一事件循环中运行的异步任务,这不是问题。但是我们需要使用 thread 模块在任务之间共享信息。

何时使用异步编程?

在以下情况下,我们可以使用异步编程。

  • 当我们想快速完成工作时。
  • 延迟涉及等待 I/O(磁盘或网络)操作,而不是计算。
  • 当许多 I/O 操作同时发生时。

asyncio 模块允许我们并行执行多个任务并高效地迭代它们,而不会阻塞应用程序的其余部分。

下面列出了一些可以与 asyncio 很好地配合使用的任务。

  • 网络爬虫。
  • 网络服务(Web 服务器和框架)
  • 并发数据库

Asyncio 中的一些重要函数

以下是进行异步编程时使用的一些基本方法。

运行 asyncio 程序

  • asyncio.run(coro, *, debug = False) - 此函数用于阻止执行delay秒。它挂起当前任务并允许另一个任务运行。delay 是一个显示秒数的参数。

示例 -

创建任务

  • create_task(coro, *, name = None) - 此函数将协程包装到任务中并调度其执行。它返回任务对象。

示例 -

睡眠

  • sleep(delay, result = None, *, loop = None) - 此函数用于阻止执行delay秒。它挂起当前任务并允许其他任务运行。delay 是一个显示秒数的参数。

示例 -

超时

  • coroutinewait_for(aw, timeout, *, loop = None) - 此函数用于在设置超时的情况下等待 aw(自动计划为任务的协程)awaitable完成。

示例 -

结论

本教程介绍了使用 Python asyncio 模块进行异步编程的概念。asyncio 为我们提供了对上下文切换发生时间的编程控制。这意味着我们可以处理多线程编程中出现的许多复杂问题。

这是一个强大而有价值的工具,但仅适用于异步类型的编程。我们已经讨论了协程和任务及其各自的示例。我们还讨论了管理事件循环以及在 Python 中读写流数据。它还包括基本方法。


下一个主题Python main() 函数