Python教程-Python 异步编程 - asyncio 和 await
asyncio 模块。asyncio 模块提供了出色的功能,允许我们编写更高效的 Python 异步应用程序。我们将探讨如何在 Python 中管理异步事件循环。在深入研究这个主题之前,让我们先了解什么是异步编程。
什么是异步编程?
在同步编程中,方法是按顺序执行一个任务。如果一个函数依赖于另一个函数的输出,它必须等待该函数执行完毕。程序在执行该函数期间实际上停止了。这意味着一次只能执行一个程序。
这会降低程序的执行速度,因为它被迫停下来等待某事完成。系统中有许多处理器,因此在等待时执行其他任务而不是让其空闲是资源的浪费。
为了克服这一问题,引入了异步编程的概念。它的行为不同;它也一次只执行一个任务。但系统不会等待执行完成后再继续下一步。
这意味着处理器不会空闲,如果程序还没有完成运行而在其他地方运行。
在本教程中,我们将解释为什么需要这种类型的编程。
什么是 asyncio - 异步 I/O?
asyncio 是一个用于使用 async/await 运行并发代码的 Python 库。它是 Python 异步框架的基础,提供连接库、网络和 Web 服务器、分布式任务队列、高性能等功能。
该模块提供了围绕事件循环工作的框架,还负责 I/O 和系统事件等方面的处理。
协程和任务
asyncio 是一个用于使用 async/await 运行并发代码的 Python 库。它是 Python 异步框架的基础,提供连接库、网络、Web 服务器、数据库分布式任务队列、高性能等功能。
该模块提供了围绕事件循环工作并处理 I/O 和系统事件等方面的框架。
示例 - 1
import asyncio
async def main():
print ("Waiting 5 seconds. ")
for _ in range(5):
await asyncio.sleep(1)
print ("Hello")
print ("Finished waiting.")
asyncio.run(main())
输出:
Waiting 5 seconds.
Hello
Hello
Hello
Hello
Hello
Finished waiting.
说明 -
在上面的代码中:
- 我们导入 asyncio 模块以获得 Python 的 async 功能。
- 然后创建了一个 main() 函数,并在其前面加上了 async 关键字。这将允许程序以异步方式运行任务。
- 我们使用 for 循环并调用 sleep() 方法,它强制我们等待 1 秒。
- 程序在一秒后打印 "你好"。
- 程序应该有一个 .run() 函数以及一个 .main() 函数。
我们还可以安排任务或对象来绑定协程并帮助它们运行。让我们理解以下示例。
示例 - 2
import asyncio
import time
async def execute(delay, value):
await asyncio.sleep(delay)
print(value)
async def main():
print(f"started at {time.strftime('%X')}")
await execute(1, 'hello')
await execute(2, 'world')
print(f"finished at {time.strftime('%X')}")
asyncio.run(main())
输出:
started at 11:11:54
hello
world
finished at 11:11:57
说明 -
在上面的代码中:
- 我们导入了 asyncio 和 time 模块。
- 然后我们定义了 execute() 函数,带有延迟和值参数。它使用 sleep() 方法打印延迟时间。
- 在 main() 函数中,我们传递了两个参数:第一个是延迟时间,第二个是要打印的值。
- 程序开始执行,并打印确切的执行时间,然后打印 '你好',然后等待两秒,打印 '世界',然后停止。
现在,让我们对上面的代码进行一些更改并查看结果。
示例 - 3 创建任务
async def execute(delay, value):
await asyncio.sleep(delay)
print(value)
async def main():
# Using asyncio.create_task() method to run coroutines concurrently as asyncio
task1 = asyncio.create_task(
execute(1, 'hello'))
task2 = asyncio.create_task(
execute(2, 'world'))
print(f"started at {time.strftime('%X')}")
# Wait until both tasks are completed (should take
# around 2 seconds.)
await task1
await task2
print(f"finished at {time.strftime('%X')}")
asyncio.run(main())
输出:
started at 15:43:30
hello
world
finished at 15:43:32
我们可以看到它比前一个程序快 1 秒。create.task() 方法将在事件循环中运行,并将其结果放入任务中。我们已经安排了两个任务,并使用 await 返回它们。
在 Python 中管理异步事件循环
Asyncio 也用于管理异步事件循环。事件循环是一个运行异步函数和回调的对象。当我们想要执行协程时,事件很重要,当我们运行 asyncio.run() 方法时,事件循环对象会自动创建。要实现更高级的服务器,我们需要更低级的事件循环访问。我们需要直接处理事件循环的内部。
事件循环具有以下功能。
- 它可以注册、执行和取消延迟调用(异步函数)
- 它可以为通信创建客户端和服务器传输
- 它可以创建用于与另一个程序通信的子进程和传输。
- 将函数调用委派给线程池。
让我们看看以下示例。
示例 -
import asyncio
async def speech_async():
print('This is a asynchronicity!')
loop = asyncio.get_event_loop()
loop.run_until_complete(speech_async())
loop.close()
输出:
This is a asynchronicity!
事件循环通过获取 asyncio.get_event_loop() 开始,安排并运行异步任务,然后在运行结束后关闭事件循环。
用 Python 中的 Stream 读写数据
asyncio 模块提供了用于执行高级网络 I/O 的 stream,它可以用作网络请求的服务器。它最适合长时间运行的网络操作,在该操作中,应用程序会阻塞等待其他资源返回结果。
asyncio 有两个类,StreamReader 和 StreamWriter,这些类用于高级网络读取和写入。要从网络读取,我们需要使用 asyncio.open_connection() 打开网络。StreamReader 和 StreamWriter 对象函数返回元组,我们可以使用 .read() 和 .write() 方法对每个连接进行操作。
asyncio.start_server() 方法用于接收来自远程主机的连接。此函数需要回调函数 client_connected_cb 作为参数。每当函数接收到请求时,都会调用它。
在 Python 中同步任务
如前所述,异步程序运行独立,但有时我们希望它们互相通信。asyncio 模块为我们提供了队列和各种其他方法来建立任务之间的同步。
让我们了解以下实现方法。
- 队列 - asyncio 队列可使异步函数排队以由其他异步函数使用。例如 - 工作负载分布在函数上,以根据其行为进行分配。
- 同步原语 - asyncio 的功能锁、事件、条件和信号量等行为类似于常规的 Python 对应物。
这里应该始终记住这些方法不是线程安全的。这对于在同一事件循环中运行的异步任务不是问题。但我们需要使用线程模块在任务之间共享信息。
何时使用异步编程?
在以下情况下,我们可以使用异步编程。
- 当我们希望在较短的时间内完成工作。
- 延迟涉及等待 I/O(磁盘或网络)操作,而不涉及计算。
- 当许多 I/O 操作同时发生时。
asyncio 模块允许我们并行执行多个任务并高效地迭代它们,而不会阻塞应用程序的其余部分。
以下是一些适合使用 asyncio 的任务。
- 网页抓取。
- 网络服务(Web 服务器和框架)
- 并发数据库
异步编程中的一些重要函数
以下是在进行异步编程时使用的一些重要方法。
运行 asyncio 程序
- asyncio.run(coro, *, debug = False) - 此函数用于阻塞执行 delay 秒。它挂起当前任务并允许其他任务运行。delay 是一个参数,表示秒数。
示例 -
async def main():
await asyncio.sleep(1)
print('hello')
asyncio.run(main())
创建任务
- create_task(coro, *, name = None) - 此函数将协程包装成任务,并调度其执行。它返回任务对象。
示例 -
async def coro():
...
# In Python 3.7+
task = asyncio.create_task(coro())
...
task = asyncio.ensure_future(coro())
等待
- sleep(delay, result = None, *, loop = None) - 此函数用于阻塞执行 delay 秒。它挂起当前任务并允许其他任务运行。delay 是一个参数,表示秒数。
示例 -
import asyncio
async def main():
for _ in range(3):
await asyncio.sleep(1)
print ("Hello")
asyncio.run(main())
超时
- coroutinewait_for(aw, timeout, *, loop = None) - 此函数用于等待 aw(协程自动调度为任务)的完成,带有超时。
示例 -
async def myfunc():
# Sleep for ten minutes
await asyncio.sleep(600)
print('hello!')
async def main():
# Wait for at most 1 second
try:
await asyncio.wait_for(myfunc(), timeout=1.0)
except asyncio.TimeoutError:
print('timeout!')
asyncio.run(main())
结论
本教程介绍了使用 Python asyncio 模块进行异步编程的概念。asyncio 允许我们在使用上下文切换时对程序进行编程控制。这意味着我们可以处理许多与线程编程相关的复杂问题。
它是一个强大而有价值的工具,但只适用于异步类型的编程。我们已经讨论了协程和任务以及它们各自的示例。我们还讨论了如何管理事件循环以及在 Python 中读写数据流的内容。还包括了一些重要的方法。