基本概念
- 同步代码:在同步代码中,每个操作都按顺序执行,下一个操作必须等待前一个操作完成。这可能会导致阻塞,尤其是在进行I/O操作(如文件读写、网络请求等)时。
- 异步代码:异步代码允许在等待某些操作完成时继续执行其他操作,而不阻塞整个程序。这在处理I/O密集型任务时特别有用,可以提高程序的效率和响应速度。
async 和 await
- async 关键字:
-
- 用于定义一个协同程序(coroutine)。
- 协同程序是一种可以在执行过程中暂停并让出控制权的函数,允许其他协同程序运行。
- 在定义函数时,使用 async def 来标记这个函数是异步的。
- await 关键字:
-
- 用于在协同程序中等待另一个协同程序的完成。
- 当执行到 await 时,如果结果尚未准备好,协同程序会暂停并让出控制权给事件循环,直到结果准备好再继续执行。
- 只能在 async 定义的协同程序中使用。
作用与示例
作用
- 提高效率:异步编程允许在等待I/O操作完成时继续执行其他任务,避免阻塞,提高效率。
- 增强响应性:在网络请求或文件操作等耗时操作中,异步编程可以保持应用程序的响应性。
- 资源优化:通过非阻塞I/O操作,可以更好地利用系统资源。
示例
import asyncio
# 定义一个异步函数
async def fetch_data():
print("开始获取数据...")
await asyncio.sleep(2) # 模拟一个耗时操作
print("数据获取完成")
return "数据内容"
# 定义另一个异步函数来使用 fetch_data
async def main():
print("启动主程序")
result = await fetch_data() # 等待 fetch_data 完成
print(f"结果: {result}")
# 运行异步程序
asyncio.run(main())
启动主程序
开始获取数据...
数据获取完成
结果: 数据内容
执行步骤
- asyncio.run(main()):启动主程序。
- 输出:启动主程序
- 进入 main 协同程序:
- result = await fetch_data():调用 fetch_data 并等待其完成。
- 进入 fetch_data 协同程序:
- 输出:开始获取数据...
- await asyncio.sleep(2):程序暂停2秒钟,让出控制权给事件循环。
- 2秒钟后,fetch_data 恢复执行:
- 输出:数据获取完成
- 返回值:"数据内容"
- main 恢复执行:
- result = await fetch_data() 获取返回值 "数据内容"
- 输出:结果: 数据内容
解释
- async def:定义异步函数。
- await:在异步函数中等待另一个异步操作完成。
- asyncio.sleep(2):模拟一个耗时操作(暂停2秒),期间不会阻塞整个程序。
- asyncio.run(main()):启动并运行 main 协同程序,直到其完成。
在这个示例中:
- fetch_data 是一个异步函数,用 async def 定义。它在模拟一个耗时操作(例如网络请求)时使用 await asyncio.sleep(2) 暂停执行2秒钟。
- main 是另一个异步函数,它调用 fetch_data 并使用 await 等待 fetch_data 的结果。
- asyncio.run(main()) 用于运行主程序。
背后的机制
- 事件循环:异步编程的核心是事件循环(event loop)。事件循环是一个程序结构,用于循环地检查并处理事件或任务。当一个任务被挂起时,事件循环会切换到另一个任务继续执行。
- 任务(Tasks):任务是协同程序的具体执行实例,表示一个协同程序的执行过程。
- Future和Promise:这两个概念在异步编程中也很常见,表示一个将来会完成的操作结果。
总结
- async 用于定义一个协同程序,使得函数变为异步函数。
- await 用于等待协同程序完成,如果结果未准备好会让出控制权给事件循环。
- 异步编程通过非阻塞的I/O操作,提高程序的效率和响应性,特别适用于I/O密集型任务。