python 异步编程 -- 理解await
什么是await
await 是 Python 异步编程中的一个关键字,它用于暂停一个协程(coroutine)的执行,直到其等待的异步操作完成。await 表达式会交出程序的控制权,允许事件循环(event loop)去执行其他的任务。当等待的异步操作完成后,事件循环会恢复该协程的执行。
简单来说,await 就是告诉程序:“嘿,这个操作需要一些时间,你先去忙别的,等它好了再叫我回来继续。”
import asyncio
async def say_after(delay, what):
await asyncio.sleep(delay)
print(what)
async def main():
print("程序开始")
# 暂停main()的执行,直到say_after(1, 'hello')完成
await say_after(1, 'hello')
# 暂停main()的执行,直到say_after(2, 'world')完成
await say_after(2, 'world')
print("程序结束")
asyncio.run(main())
# 输出:
# 程序开始
# (等待1秒)
# hello
# (等待2秒)
# world
# 程序结束
await能够wait 什么对象
await 关键字后面可以跟多种可等待对象(awaitable objects),主要包括以下三种:
- 协程对象(Coroutine Object): 直接调用一个
async def函数返回的对象。 - Task 对象: 通过
asyncio.create_task()或loop.create_task()创建的任务,用于并发执行协程。 - Future 对象: 一个代表未来某个时刻会产生结果的底层抽象。Task 是 Future 的一个子类。
import asyncio
# 1. 等待一个协程对象
async def my_coroutine():
print("Coroutine is running")
await asyncio.sleep(1)
return "Coroutine finished"
async def main_coro():
result = await my_coroutine() # 直接等待协程
print(result)
# 2. 等待一个Task对象
async def main_task():
task = asyncio.create_task(my_coroutine())
result = await task # 等待Task
print(result)
# asyncio.run(main_coro())
asyncio.run(main_task())
await 只能在async方法内使用
这是一个语法规定。await 关键字只能在由 async def 定义的异步函数内部使用。如果在普通函数(def)中使用 await,Python 解释器会抛出 SyntaxError。
这是因为 await 的机制依赖于事件循环来管理协程的暂停和恢复,而只有 async 函数才能被事件循环所调度。
import asyncio
async def async_func():
await asyncio.sleep(1) # 正确
def sync_func():
# await asyncio.sleep(1) # 错误: SyntaxError: 'await' outside async function
pass
await 的2大作用
1. 等待async 方法完成才执行后面的代码
在一个异步函数内部,await 确保了代码的执行顺序。当程序遇到 await some_async_operation() 时,它会暂停当前函数的执行,直到 some_async_operation 完成,然后才会继续执行 await 后面的代码。这使得异步代码的逻辑可以像同步代码一样顺序地编写。
import asyncio
import time
async def step_1():
print("第一步开始")
await asyncio.sleep(2)
print("第一步完成")
async def step_2():
print("第二步开始")
await asyncio.sleep(1)
print("第二步完成")
async def main():
start_time = time.time()
await step_1() # 等待 step_1 完成
await step_2() # 然后再执行和等待 step_2 完成
end_time = time.time()
print(f"总耗时: {end_time - start_time:.2f} 秒")
asyncio.run(main())
# 输出:
# 第一步开始
# (等待2秒)
# 第一步完成
# 第二步开始
# (等待1秒)
# 第二步完成
# 总耗时: 3.00 秒
2. 遇到aysnc 方法的IO阻塞时会切换去执行同级时间循环中别的任务
这是 await 最核心的功能。当 await 等待一个 I/O 密集型操作(如网络请求、文件读写、asyncio.sleep())时,它并不会像 time.sleep() 那样阻塞整个线程。相反,它会将控制权交还给事件循环。事件循环此时可以去检查是否有其他已经就绪的任务,并执行它们。这就是所谓的“协作式多任务”。
import asyncio
import time
async def task_a():
print("任务A开始,准备等待2秒")
await asyncio.sleep(2)
print("任务A结束")
async def task_b():
print("任务B开始,准备等待1秒")
await asyncio.sleep(1)
print("任务B结束")
async def main():
start_time = time.time()
# 使用 asyncio.gather 并发运行 task_a 和 task_b
# 当 task_a await时,事件循环会切换到 task_b
await asyncio.gather(task_a(), task_b())
end_time = time.time()
print(f"总耗时: {end_time - start_time:.2f} 秒")
asyncio.run(main())
# 输出:
# 任务A开始,准备等待2秒
# 任务B开始,准备等待1秒
# (等待1秒后)
# 任务B结束
# (再等待1秒后)
# 任务A结束
# 总耗时: 2.01 秒
在这个例子中,总耗时约等于最长的那个任务的耗时(2秒),而不是所有任务耗时之和(3秒),证明了任务是并发执行的。
async 方法内可以有多个await 去等待多个asyns 方法
在一个 async 函数中,你可以串联多个 await 表达式。它们会按照代码的书写顺序依次执行。前一个 await 的异步操作必须完成后,后一个 await 才会开始。
这与使用 asyncio.gather 并发执行是不同的。串行 await 适用于有依赖关系的异步操作。
import asyncio
async def get_data(source):
print(f"开始从 {source} 获取数据...")
await asyncio.sleep(1)
print(f"完成从 {source} 获取数据")
return f"Data from {source}"
async def process_data(data):
print(f"开始处理数据: {data}...")
await asyncio.sleep(0.5)
print(f"完成处理数据: {data}")
return f"Processed {data}"
async def main():
# 串行执行:获取数据 -> 处理数据
data = await get_data("API 1")
processed_data = await process_data(data)
print(f"最终结果: {processed_data}")
asyncio.run(main())
# 输出:
# 开始从 API 1 获取数据...
# (等待1秒)
# 完成从 API 1 获取数据
# 开始处理数据: Data from API 1...
# (等待0.5秒)
# 完成处理数据: Data from API 1
# 最终结果: Processed Data from API 1
总结
await是一个关键字,用于在async def函数中调用和等待可等待对象(协程、Task、Future)。await的核心作用是暂停当前协程,将控制权交还给事件循环,以便执行其他任务,从而实现非阻塞的并发。await保证了在同一个协程内部,代码是按顺序执行的,使得异步逻辑更易于理解和编写。await只能在async函数中使用,这是其与事件循环协作的基础。- 多个
await串行执行,适用于有前后依赖的异步操作;若要并发执行无依赖的操作,应使用asyncio.gather或asyncio.create_task。
更多推荐



所有评论(0)