Python 异步协程:基础

74 阅读3分钟

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​库,可以编写高效、清晰的并发代码。掌握协程编程需要理解以下核心概念:

  • 协程函数的定义和调用
  • 事件循环的工作原理
  • 并发执行多个任务的方法
  • 资源管理和异常处理

七、引用