Python 异步协程:基础
一、基础概念
1. 什么是协程?
协程是一种特殊的函数,可以在执行过程中暂停并恢复,允许其他代码在暂停期间运行。在Python中,使用async def定义协程函数。
async def my_coroutine():
print("协程开始")
await asyncio.sleep(1) # 暂停执行1秒
print("协程结束")
2. 与线程的区别
- 线程:由操作系统调度,上下文切换开销大
- 协程:由程序主动控制切换,单线程执行,无切换开销
二、async/await 语法
1. 基本使用
下面是一个简单的协程示例,展示了如何定义和运行协程:
import asyncio
async def greet(name):
print(f"你好, {name}!")
await asyncio.sleep(1) # 模拟耗时操作
print(f"{name}, 再见!")
# 运行协程
asyncio.run(greet("Alice"))
2. 并发执行多个协程
使用asyncio.gather()可以并发运行多个协程:
import asyncio
import time
async def task(name, delay):
print(f"{name} 开始")
await asyncio.sleep(delay)
print(f"{name} 结束")
return delay
async def main():
start_time = time.time()
# 并发运行三个任务
results = await asyncio.gather(
task("任务1", 2),
task("任务2", 1),
task("任务3", 3)
)
end_time = time.time()
print(f"总耗时: {end_time - start_time:.2f}秒")
print("结果:", results)
asyncio.run(main())
输出结果:
任务1 开始
任务2 开始
任务3 开始
任务2 结束
任务1 结束
任务3 结束
总耗时: 3.00秒
结果: [2, 1, 3]
三、异步I/O操作
1. 模拟I/O密集型任务
以下示例展示了如何使用协程处理多个I/O密集型任务:
import asyncio
import random
async def process_item(item):
print(f"处理项目: {item}")
# 模拟网络请求或文件读写
await asyncio.sleep(random.uniform(0.5, 2.0))
print(f"完成处理: {item}")
return item
async def main():
items = ["数据1", "数据2", "数据3", "数据4"]
# 创建任务列表
tasks = [process_item(item) for item in items]
# 并发执行所有任务
results = await asyncio.gather(*tasks)
print("所有结果:", results)
asyncio.run(main())
2. 使用异步上下文管理器
对于需要管理资源的场景(如数据库连接、文件操作),可以使用async with:
import asyncio
class AsyncDatabaseConnection:
async def __aenter__(self):
print("连接到数据库...")
await asyncio.sleep(0.5) # 模拟连接耗时
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
print("关闭数据库连接...")
await asyncio.sleep(0.5) # 模拟关闭耗时
async def query(self, statement):
print(f"执行查询: {statement}")
await asyncio.sleep(1)
return {"result": "查询结果"}
async def main():
async with AsyncDatabaseConnection() as db:
result = await db.query("SELECT * FROM users")
print("查询结果:", result)
asyncio.run(main())
四、高级应用
1. 超时控制
使用asyncio.wait_for()可以为协程设置超时时间:
import asyncio
async def slow_operation():
await asyncio.sleep(3)
return "完成"
async def main():
try:
# 设置超时时间为2秒
result = await asyncio.wait_for(slow_operation(), timeout=2)
print(result)
except asyncio.TimeoutError:
print("操作超时")
asyncio.run(main())
2. 任务取消
可以通过Task.cancel()方法取消正在运行的任务:
import asyncio
async def long_running_task():
try:
print("开始长时间任务...")
await asyncio.sleep(10)
print("任务完成")
except asyncio.CancelledError:
print("任务被取消")
raise
async def main():
task = asyncio.create_task(long_running_task())
# 运行2秒后取消任务
await asyncio.sleep(2)
task.cancel()
try:
await task
except asyncio.CancelledError:
print("任务已被取消")
asyncio.run(main())
3. 处理阻塞操作
对于无法避免的阻塞操作,可以使用run_in_executor在线程池中执行:
import asyncio
import time
from concurrent.futures import ThreadPoolExecutor
def blocking_function(seconds):
print(f"阻塞操作开始,需要{seconds}秒")
time.sleep(seconds)
print("阻塞操作完成")
return seconds
async def main():
loop = asyncio.get_running_loop()
# 在线程池中执行阻塞操作
future1 = loop.run_in_executor(None, blocking_function, 2)
future2 = loop.run_in_executor(None, blocking_function, 1)
# 同时执行其他异步操作
async_task = asyncio.create_task(asyncio.sleep(1.5))
# 等待所有任务完成
results = await asyncio.gather(future1, future2, async_task)
print("所有结果:", results)
asyncio.run(main())
五、最佳实践
1. 选择合适的场景
- 适合异步的场景:I/O密集型任务(网络请求、文件读写)
- 不适合的场景:CPU密集型任务(大量计算)
2. 异常处理
使用try/except捕获和处理协程中的异常:
async def task_that_might_fail():
raise ValueError("出错了!")
async def main():
try:
await task_that_might_fail()
except ValueError as e:
print(f"错误: {e}")
3. 调试技巧
- 使用
asyncio.run(..., debug=True)启用调试模式 - 避免在协程中使用阻塞调用
- 使用日志记录执行流程
六、总结
异步协程是Python处理I/O密集型任务的强大工具,通过async/await语法和asyncio库,可以编写高效、清晰的并发代码。掌握协程编程需要理解以下核心概念:
- 协程函数的定义和调用
- 事件循环的工作原理
- 并发执行多个任务的方法
- 资源管理和异常处理
七、引用