Python 异步协程asyncio详解
简介
在传统的同步编程中,我们调用一个函数后,只有等它返回了结果,我们才能继续往下执行。而在异步编程中,我们不需要等待这个函数返回结果,可以立即执行下一个函数。
Python 提供了 asyncio
模块来支持异步编程,其中的 async
和 await
关键字可以方便地定义异步函数和协程。本文将详细介绍 asyncio
的使用方法和一些实例。
asyncio 模块基本概念
在开始学习 asyncio
之前,我们先了解一些异步编程相关的基本概念。
协程(Coroutine)
协程是一种特殊的函数,它可以被中断和恢复,并且可以多次执行。在 asyncio
中,协程使用 async def
来定义。
任务(Task)
任务是对协程的封装,它可以通过 asyncio.create_task()
函数来创建。任务的运行可以通过 await
关键字进行中断,也可以通过 await asyncio.gather()
等待多个任务同时完成。
事件循环(Event Loop)
事件循环是异步编程的核心,它负责调度协程和任务的执行。在 asyncio
中,事件循环可以通过 asyncio.get_event_loop()
获取,并通过 loop.run_until_complete()
来启动。
异步函数(async function)
异步函数是一种特殊的函数,可以使用 await
关键字来中断其执行。
使用 asyncio
下面是一个简单的例子,展示了如何使用 asyncio
实现异步编程。
import asyncio
async def hello():
print("Hello")
await asyncio.sleep(1)
print("World")
loop = asyncio.get_event_loop()
loop.run_until_complete(hello())
运行以上代码,输出如下:
Hello
World
在上面的例子中,我们定义了一个名为 hello
的异步函数,其中使用 await asyncio.sleep(1)
来模拟一个耗时的操作。然后我们创建了一个事件循环,并通过 loop.run_until_complete()
来运行 hello
函数。输出结果显示 Hello
和 World
几乎是同时打印的,说明 asyncio
实现了真正的异步执行。
异步IO操作
异步编程的主要目标是处理 IO 操作。在传统的同步编程中,当我们进行一个 IO 操作时,程序会被阻塞,只有等到 IO 操作完成后才能继续执行。而异步编程可以在等待 IO 操作时继续执行其他任务,从而提高程序的并发性能。
使用 asyncio
可以方便地实现异步 IO 操作。下面是一个例子,展示了如何使用 asyncio
进行文件读写操作。
import asyncio
async def read_file(file):
with open(file, 'r') as f:
return await f.read()
async def write_file(file, text):
with open(file, 'w') as f:
await f.write(text)
async def main():
text = await read_file('input.txt')
await write_file('output.txt', text.upper())
asyncio.run(main())
以上代码中,我们定义了两个异步函数 read_file
和 write_file
,分别用于异步读取文件和异步写入文件。然后我们定义了一个 main
函数,它使用 await
关键字来等待读取文件操作完成后再进行写入文件操作。最后我们使用 asyncio.run()
来运行 main
函数。
并发执行多个任务
在异步编程中,经常需要同时执行多个任务,以提高程序的并发性能。asyncio
提供了多个函数来实现并发执行多个任务的功能。
asyncio.gather()
asyncio.gather()
函数可以同时运行多个任务,并等待它们全部完成。下面是一个例子:
import asyncio
async def task1():
await asyncio.sleep(1)
print("Task 1 completed")
async def task2():
await asyncio.sleep(2)
print("Task 2 completed")
async def main():
await asyncio.gather(task1(), task2())
asyncio.run(main())
以上代码中,我们定义了两个异步任务 task1
和 task2
,它们分别等待 1 秒和 2 秒。然后在 main
函数中使用 asyncio.gather()
同时运行这两个任务,并等待它们全部完成。输出结果如下:
Task 1 completed
Task 2 completed
asyncio.wait()
asyncio.wait()
函数可以同时运行多个任务,并等待其中一个任务完成。下面是一个例子:
import asyncio
async def task1():
await asyncio.sleep(1)
return "Task 1 completed"
async def task2():
await asyncio.sleep(2)
return "Task 2 completed"
async def main():
done, pending = await asyncio.wait([task1(), task2()], return_when=asyncio.FIRST_COMPLETED)
for task in done:
print(task.result())
asyncio.run(main())
以上代码中,我们定义了两个异步任务 task1
和 task2
,它们分别返回字符串表示任务完成。然后在 main
函数中使用 asyncio.wait()
同时运行这两个任务,并等待其中一个任务完成。输出结果如下:
Task 1 completed
asyncio.as_completed()
asyncio.as_completed()
函数可以同时运行多个任务,并按照完成的顺序返回一个迭代器。下面是一个例子:
import asyncio
async def task1():
await asyncio.sleep(1)
return "Task 1 completed"
async def task2():
await asyncio.sleep(2)
return "Task 2 completed"
async def main():
tasks = [task1(), task2()]
for task in asyncio.as_completed(tasks):
print(await task)
asyncio.run(main())
以上代码中,我们定义了两个异步任务 task1
和 task2
,它们分别返回字符串表示任务完成。然后在 main
函数中使用 asyncio.as_completed()
同时运行这两个任务,并按照完成的顺序返回一个迭代器。输出结果如下:
Task 1 completed
Task 2 completed
异常处理
在异步编程中,异常的处理比同步编程更加复杂。asyncio
提供了一些机制来方便地处理异常。
使用 try-except
我们可以使用 try-except 块来捕获异步任务中发生的异常。下面是一个例子:
import asyncio
async def task():
await asyncio.sleep(1)
raise ValueError("Error in task")
async def main():
try:
await task()
except ValueError as e:
print("Caught exception:", e)
asyncio.run(main())