Python,作为一门流行的编程语言,不仅具备清晰简洁的语法和强大的生态系统,还在异步编程领域拥有丰富而灵活的工具,其中包括协程、异步IO以及asyncio库。本文将深入探讨这些概念的本质,解释它们的原理,并通过实际示例演示如何应用它们,帮助我们更好地理解和利用Python的异步编程能力。
概念介绍
Python协程:
- 协程是一种轻量级的线程,用于非阻塞异步编程。
- 通过async和await关键字定义,使得函数可以在执行中暂停和恢复。
- 协程可以用于处理高并发的I/O密集型任务,而无需使用多线程或多进程的复杂性。
- 通常与异步IO一起使用,以实现高效的非阻塞IO操作。
异步IO:
- 异步IO是一种编程模型,用于处理非阻塞IO操作,以提高程序的并发性能。
- 它允许一个程序同时处理多个IO操作,而不需要等待每个IO操作完成。
- 在Python中,异步IO通常与协程一起使用,以实现高效的非阻塞IO编程。
asyncio:
- asyncio是Python标准库中的异步IO库,用于编写基于协程的异步程序。
- 它提供了事件循环(event loop),用于调度和执行协程任务。
- asyncio包含了许多工具和函数,用于处理异步IO操作,例如网络通信、文件IO等。
- asyncio是Python中异步编程的核心库,可以用于构建高性能的异步应用程序,如Web服务器、聊天应用程序等。
协程(Coroutines)
协程是一种轻量级的线程,它允许函数在执行过程中暂停并恢复。与常规函数不同,协程具有多个入口点,可以在函数内部的任何位置暂停和继续执行。Python的协程通过async和await关键字来定义和管理。
基本概念
在Python中,协程的基本概念如下:
- async def:通过在函数定义前添加async关键字,可以将普通函数变成协程函数。协程函数可以在执行过程中暂停。
- await:await关键字用于在协程中等待另一个协程或异步操作完成。当执行到await语句时,协程将暂停,直到等待的操作完成。
示例
下面是一个简单的协程示例,演示了如何使用协程来实现异步任务:
代码语言:javascript复制import asyncio
async def hello(arg):
print("Hello : ",arg)
if arg == "SRE 1":
await asyncio.sleep(2)
else:
await asyncio.sleep(1)
print("World : ",arg)
print("bingo!!")
async def main():
await asyncio.gather(hello("SRE 1"), hello("SRE 2"), hello("SRE 3"))
asyncio.run(main())
运行可以看到日志
代码语言:javascript复制Hello : SRE 1
Hello : SRE 2
Hello : SRE 3
World : SRE 2
bingo!!
World : SRE 3
bingo!!
World : SRE 1
bingo!!
在这个示例中,hello函数是一个协程,通过await asyncio.sleep(1)来模拟一个耗时的操作。main函数使用await asyncio.gather()来同时运行多个协程。
异步IO(Asynchronous IO)
异步IO是一种编程模型,用于处理非阻塞的IO操作。它使程序能够在执行IO操作时继续执行其他任务,而不必等待IO操作完成。
基本概念
异步IO的核心概念包括:
- 非阻塞IO:异步IO允许执行非阻塞的IO操作,这意味着程序在等待IO完成时不会被阻塞。
- 事件循环:异步IO通常使用事件循环来管理协程和异步任务的调度。事件循环负责将协程放入等待IO的队列,并在IO完成时恢复它们的执行。
示例
以下示例演示了如何使用异步IO进行文件读取操作:
代码语言:javascript复制import asyncio
# 定义一个异步函数,模拟一个非阻塞的IO操作
async def non_blocking_io_operation(arg):
print("开始非阻塞IO操作")
if arg == "task1":
await asyncio.sleep(2) # 模拟IO操作,这里使用await来模拟异步操作
else:
await asyncio.sleep(5)
print(f"完成调用:{arg} 的非阻塞IO操作")
# 创建一个事件循环
async def main():
# 将非阻塞IO操作放入事件循环中
task1 = asyncio.create_task(non_blocking_io_operation("task1"))
task2 = asyncio.create_task(non_blocking_io_operation("task2"))
# 等待所有任务完成
await task1
print("完成task1 IO执行!")
await task2
print("完成task2 IO执行!")
# 启动事件循环
if __name__ == "__main__":
asyncio.run(main())
执行输出日志
代码语言:javascript复制开始非阻塞IO操作
开始非阻塞IO操作
完成调用:task1 的非阻塞IO操作
完成task1 IO执行!
完成调用:task2 的非阻塞IO操作
完成task2 IO执行!复制
asyncio库
asyncio 是异步 I/O 的缩写。它是一个 Python 库,允许我们使用异步编程模型运行代码。这让我们可以同时处理多个 I/O 操作,同时仍然允许我们的应用程序保持响应能力。
Python 3.4 引入了 asyncio 库,Python 3.5 生成了 async 和await 关键字以方便地使用它。这些新增功能允许所谓的异步编程。
基本概念
asyncio库的基本概念包括:
- 事件循环(Event Loop):事件循环是异步程序的核心,负责调度协程和处理异步任务的完成。
- asyncio.run():这个函数用于运行主协程,它在Python 3.7及更高版本中可用。
- asyncio.create_task():用于创建并调度协程任务。
简单示例
以下示例展示了如何使用asyncio库来并发执行多个协程任务:
代码语言:javascript复制import asyncio
async def task1():
print("into task1 ....")
await asyncio.sleep(2)
print("Task 1 completed")
async def task2():
print("into task2 ...")
await asyncio.sleep(1)
print("Task 2 completed")
async def main():
task1_handle = asyncio.create_task(task1())
task2_handle = asyncio.create_task(task2())
await task1_handle
await task2_handle
asyncio.run(main())
代码语言:javascript复制在这个示例中,我们使用asyncio.create_task()创建了两个协程任务,并且可以并发执行它们。await关键字用于等待任务完成。
异步事件循环
事件循环是 asyncio 应用程序的核心,负责处理所有正在运行的任务。事件循环支持多任务处理。当一个函数被挂起时,控制权返回到循环,然后循环找到另一个函数来启动或恢复。
使用事件循环的语法
代码语言:javascript复制loop = asyncio.get_event_loop()
loop.run_until_complete(my_coroutine())
loop.close()
示例
代码语言:javascript复制import asyncio
import time
async def cor1():
print("cor 1 started")
for i in range(5):
await asyncio.sleep(1.5)
print("cor1", i)
async def cor2():
print("cor 2 started")
for i in range(8):
await asyncio.sleep(1)
print("cor2", i)
loop = asyncio.get_event_loop()
cors = asyncio.wait([cor1(), cor2()])
loop.run_until_complete(cors)复制
运行可以得到输出
代码语言:javascript复制cor 2 started
cor 1 started
cor2 0
cor1 0
cor2 1
cor1 1
cor2 2
cor2 3
cor1 2
cor2 4
cor1 3
cor2 5
cor2 6
cor1 4
cor2 7
任务
任务用于同时调度协程。我们使用函数 create_task() 创建一个任务。使用此函数将协程包装到任务中。
示例
代码语言:javascript复制import asyncio
import time
async def main():
# 使用 asyncio.create_task() 方法创建任务
task1 = asyncio.create_task(foo('task 1 run'))
task2 = asyncio.create_task(foo('task 2 run'))
print(f"started at {time.strftime('%X')}")
# 等待所有任务执行完成
await task1
await task2
print(f"finished at {time.strftime('%X')}")
async def foo(text):
print(text)
await asyncio.sleep(1)
asyncio.run(main())
得到输出
代码语言:javascript复制started at 17:35:47
task 1 run
task 2 run
finished at 17:35:48
同时运行任务
为了执行并发执行,我们使用函数 Gather 如下。可等待的 asyncio.gather(*aws, return_exceptions=False)
该函数采用任意数量的可等待项(协程、任务等)作为参数。
示例
代码语言:javascript复制import asyncio
# 定义一个异步函数,用于打印文本并在每次打印后等待一段时间
async def printing(task, text):
while True:
print(task, text)
try:
await asyncio.sleep(0.50) # 等待0.50秒,模拟异步操作
except asyncio.CancelledError:
break # 如果收到取消请求,跳出循环结束任务
async def main():
try:
# 使用 asyncio.wait_for 设置一个超时时间为 3 秒,同时并发运行三个打印任务
await asyncio.wait_for(
asyncio.gather(
printing("A", "task 1"),
printing("B", "task 2"),
printing("C", "task 3")
), 3 # 超时时间设置为 3 秒
)
except asyncio.TimeoutError:
print("Time over") # 如果超时,打印 "Time over" 提示
asyncio.run(main())
得到输出
代码语言:javascript复制A task 1
B task 2
C task 3
A task 1
B task 2
C task 3
A task 1
B task 2
C task 3
A task 1
B task 2
C task 3
A task 1
B task 2
C task 3
A task 1
B task 2
C task 3
Time over
_GatheringFuture exception was never retrieved
future: <_GatheringFuture finished exception=CancelledError()>
concurrent.futures._base.CancelledError
异步编程提高了应用程序性能和响应能力。Python 提供了支持异步编程的不同库。Asyncio允许我们使用 async/await 语法编写并发代码。Asyncio 由协程、任务、流、子进程、异常等高级 API 和事件循环、Futures、传输和协议等低级 API 组成。
可以查看原文:
https://mp.weixin.qq.com/s?__biz=MzA5NTgwNzY1NA==&mid=2247484074&idx=1&sn=82db976658d234dc16350a85c2709f23&chksm=90b8f363a7cf7a75c1315d099d573fc15075dfd5ef8637797cf57a7e3ae6d9250cb97e67a2ce&token=211557010&lang=zh_CN#rd