Python异步编程 – asyncio和await
asyncio模块。asyncio模块具有出色的功能,允许我们编写更高效的Python异步应用程序。我们将探讨如何在Python中管理异步事件循环。在深入探讨这个主题之前,让我们了解什么是异步编程。
什么是异步编程
在同步编程中,方法是按顺序执行一个任务。如果一个函数依赖于另一个函数的输出,它必须等待该函数执行完毕。程序实质上会在函数执行完毕之前停止。这意味着一次只能执行一个程序。
这会减慢程序的运行,因为它被强制停下来等待某件事完成。系统中有许多处理器,所以做其他任务而不是理想的是浪费资源。
为了克服这个问题,引入了异步编程概念。它的行为不同;它也一次只执行一个程序。但是系统可能不会等待完成执行才继续下一步。
这意味着处理器在程序尚未完成且仍在其他地方运行时不会空闲。
在本教程中,我们将解释为什么需要这种类型的编程。
什么是asyncio – 异步I/O
asyncio是一个Python库,用于使用async/wait运行并发代码。它是Python异步框架的基础,提供了连接库、网络和Web服务器、数据库分布式任务队列、高性能等功能。
此模块提供的框架围绕事件循环工作,并负责处理I/O和系统事件等事项。
协程和任务
asyncio是一个用于使用async/wait运行并发代码的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的 异步 功能。
- 然后创建一个 primary() 函数,并在前面加上async关键字。这将允许程序以异步的方式运行任务。
- 我们使用for循环并调用 sleep() 方法,强制程序等待1秒。
- 程序在一秒钟后打印“Hello”。
- 程序应该有一个 .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() 函数中,我们传入了两个参数:第一个是延迟时间,第二个是要打印的值。
- 程序开始执行,并打印出确切的执行时间,打印“hello”,然后等待两秒钟并打印“world”,然后停止。
现在,让我们对上面的代码进行一些更改,并查看结果。
示例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中使用流进行数据读写
asyncio模块提供了 stream ,用于执行高级网络I/O操作。它可以作为网络请求的服务器。它非常适合于长时间运行的网络操作,此时应用程序会阻塞并等待其他资源返回结果。
asyncio中有两个类 StreamReader和StreamWriter ,这些类用于在网络上进行高级读写。
要从网络中读取,我们需要使用 asyncio.open_connection() 打开网络连接。 StreamReader 和 StreamWriter 对象函数返回元组,我们将使用.read()和.write()方法对每个连接进行读写。
使用 asyncio.start_server() 方法从远程主机接收连接。此函数接受一个回调函数 client_connected_cb 作为参数。每当该函数接收到请求时,都会调用它。
Python中的同步任务
我们之前讨论过,异步程序运行独立,但有时我们希望彼此通信。asyncio模块为我们提供了队列和其他各种方法来建立任务之间的同步。
让我们了解以下实现方法。
- 队列 – asyncio队列使得异步函数能够排队Python对象,以供其他异步函数消费。例如 – 工作负载在其行为上分布在函数之间。
- 同步原语 – asyncio的特性锁、事件、条件和信号量的行为与传统的Python对应物相同。
在这里,始终要记住这些方法不是 线程安全 。这对于在同一事件循环中运行的异步任务来说并不是问题。但我们需要使用线程模块来在任务之间共享信息。
何时使用异步编程
在以下场景中,我们可以使用异步编程。
- 当我们希望在较短时间内完成工作。
- 延迟涉及等待I/O(磁盘或网络)操作,而不是计算。
- 当许多I/O操作同时发生时。
asyncio模块允许我们并行执行多个任务,并有效地迭代它们,而不会阻塞应用程序的其他部分。
以下是一些与asyncio搭配使用效果良好的任务。
- 网络爬虫。
- 网络服务(Web服务器和框架)。
- 并发数据库。
Asyncio中的一些重要函数
以下是在异步编程中使用的一些重要方法。
运行asyncio程序
asyncio.run(coro, *, debug = False)
– 这个函数用来阻塞执行 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 秒钟。它暂停当前任务,并允许其他任务运行。延迟是一个参数,表示秒数。
示例
import asyncio
async def main():
for _ in range(3):
await asyncio.sleep(1)
print ("Hello")
asyncio.run(main())
等待超时
- coroutine wait_for(aw, timeout, *, loop = None) – 这个函数用于等待aw(一个作为Task自动调度的协程) awaitable 在超时之后完成。
示例
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中使用流读取和写入数据。它还包括一些基本的方法。