Python异步编程指南:asyncio从入门到精通(Python 3.10+)

想用好Python异步编程,你得先掌握这些核心概念。下面我们从根本上解释每个关键点,并用实际代码展示如何应用。

一、基础概念层

1. 同步与异步编程

同步代码就像排队,一个任务完成才能做下一个。异步代码则让程序在等待某个操作(如网络请求)时可以去做别的事。

python 复制代码
# 同步代码:一个接一个执行
def sync_function():
    result = expensive_operation()  # 程序在这里傻等
    return result

# 异步代码:能更高效地利用时间
async def async_function():
    result = await expensive_operation()  # 等待时可以做其他事
    return result

2. 协程 (Coroutine)

协程是异步编程的主角,它可以在执行过程中暂停和恢复,让CPU有时间去处理其他任务。

python 复制代码
async def simple_coroutine():
    print("开始执行")
    await asyncio.sleep(1)  # 这里会暂停,控制权交回事件循环
    print("恢复执行")  # 1秒后继续

3. async/await 语法

这是定义和使用协程的基本语法。async def定义协程函数,await表示等待一个协程完成。

python 复制代码
async def fetch_data():
    print("开始获取数据")
    await asyncio.sleep(2)  # 模拟网络请求
    print("数据获取完成")
    return {"data": "value"}

async def main():
    result = await fetch_data()  # 等待结果
    print(result)

4. 事件循环 (Event Loop)

事件循环是整个异步系统的心脏,它协调所有协程的执行。想象它是一个不断查看待办事项的调度员,决定什么时候执行哪个任务。

python 复制代码
import asyncio

async def hello():
    print("Hello")
    await asyncio.sleep(1)
    print("World")

# 推荐方式:让asyncio自动管理事件循环
asyncio.run(hello())

5. 阻塞与非阻塞I/O

阻塞I/O会让程序停下来等待,非阻塞I/O则允许程序继续执行其他任务。

python 复制代码
# 阻塞I/O:整个程序被冻住
import time
def blocking_io():
    time.sleep(1)  # 整个程序停止1秒

# 非阻塞I/O:只有当前协程暂停
async def non_blocking_io():
    await asyncio.sleep(1)  # 只有这个协程暂停,其他协程仍能执行

二、asyncio基础层

6. asyncio.run()

这是启动异步程序的主要入口,它会创建事件循环,执行协程,然后清理现场。

python 复制代码
import asyncio

async def main():
    print("开始")
    await asyncio.sleep(1)
    print("结束")
    return "完成"

# 一行代码启动异步程序
result = asyncio.run(main())
print(result)  # 输出: 完成

7. 任务 (Task)

任务是协程的包装器,让协程能在后台运行,不必立即等待其结果。

python 复制代码
import asyncio

async def background_task():
    print("开始后台任务")
    await asyncio.sleep(2)
    print("后台任务完成")
    return "任务结果"

async def main():
    # 创建任务,立即开始执行
    task = asyncio.create_task(background_task(), name="我的后台任务")
    
    # 做其他事情
    print("主函数继续执行")
    await asyncio.sleep(1)
    
    # 需要结果时再等待
    result = await task
    print(f"得到结果: {result}")

asyncio.run(main())

8. asyncio.gather()

同时运行多个协程并等待所有结果,是并发执行的最常用方式。

python 复制代码
import asyncio

async def fetch(id):
    await asyncio.sleep(id)
    return f"结果 {id}"

async def main():
    # 并发执行三个协程,同时开始,等待所有完成
    results = await asyncio.gather(
        fetch(1),
        fetch(2),
        fetch(3)
    )
    print(results)  # ['结果 1', '结果 2', '结果 3']

asyncio.run(main())

9. asyncio.wait_for()

给异步操作设置超时限制,避免无限等待。

python 复制代码
import asyncio

async def long_operation():
    await asyncio.sleep(5)
    return "完成"

async def main():
    try:
        # 最多等2秒
        result = await asyncio.wait_for(long_operation(), timeout=2)
        print(result)
    except asyncio.TimeoutError:
        print("操作超时了")  # 会输出这个

asyncio.run(main())

10. asyncio.sleep()

异步版的sleep,不会阻塞整个程序,只会暂停当前协程。

python 复制代码
import asyncio

async def main():
    print("开始")
    await asyncio.sleep(1)  # 只暂停这个协程1秒
    print("1秒后")

asyncio.run(main())

三、并发原语层

11. Future对象

代表一个"未来会有结果"的承诺,任务的底层实现。普通开发中很少直接使用。

python 复制代码
import asyncio

async def set_future_result(future):
    await asyncio.sleep(1)
    future.set_result("Future完成了")

async def main():
    # 创建Future对象
    future = asyncio.Future()
    
    # 创建任务来设置future结果
    asyncio.create_task(set_future_result(future))
    
    # 等待future完成
    result = await future
    print(result)  # "Future完成了"

asyncio.run(main())

12. asyncio.Lock()

异步版的锁,确保同一时间只有一个协程能访问共享资源。

python 复制代码
import asyncio

async def protected_resource(lock, name):
    async with lock:  # 获取锁
        print(f"{name} 获得了锁")
        await asyncio.sleep(1)  # 模拟工作
        print(f"{name} 释放了锁")

async def main():
    lock = asyncio.Lock()
    # 三个协程竞争同一把锁
    await asyncio.gather(
        protected_resource(lock, "协程1"),
        protected_resource(lock, "协程2"),
        protected_resource(lock, "协程3")
    )

asyncio.run(main())

13. asyncio.Semaphore()

限制同时运行的协程数量,比如限制并发请求数。

python 复制代码
import asyncio

async def worker(semaphore, id):
    async with semaphore:  # 获取信号量
        print(f"工作者 {id} 开始")
        await asyncio.sleep(1)
        print(f"工作者 {id} 完成")

async def main():
    # 限制最多2个协程同时执行
    semaphore = asyncio.Semaphore(2)
    await asyncio.gather(*(worker(semaphore, i) for i in range(5)))

asyncio.run(main())

14. asyncio.Event()

用于协程间通信的简单机制,一个协程等待,另一个协程通知。

python 复制代码
import asyncio

async def waiter(event):
    print("等待信号...")
    await event.wait()  # 等待事件被设置
    print("收到信号,继续执行")

async def setter(event):
    print("3秒后发出信号")
    await asyncio.sleep(3)
    event.set()  # 设置事件,通知所有等待者

async def main():
    event = asyncio.Event()
    await asyncio.gather(waiter(event), setter(event))

asyncio.run(main())

15. asyncio.Queue()

异步版的队列,用于协程间安全地传递数据,实现生产者-消费者模式。

python 复制代码
import asyncio

async def producer(queue):
    for i in range(5):
        await asyncio.sleep(1)
        await queue.put(f"项目 {i}")
        print(f"生产了项目 {i}")

async def consumer(queue):
    while True:
        item = await queue.get()  # 等待队列中有数据
        print(f"消费了 {item}")
        queue.task_done()
        await asyncio.sleep(2)

async def main():
    queue = asyncio.Queue()
    
    # 创建生产者和消费者任务
    producers = asyncio.create_task(producer(queue))
    consumers = asyncio.create_task(consumer(queue))
    
    # 等待生产者完成
    await producers
    
    # 等待队列处理完成
    await queue.join()
    
    # 取消消费者
    consumers.cancel()

asyncio.run(main())

四、高级概念与模式层

16. 上下文管理器与async with

异步版的上下文管理器,用于资源管理,用async with语法。

python 复制代码
import asyncio

class AsyncContextManager:
    async def __aenter__(self):
        print("进入上下文")
        await asyncio.sleep(1)
        return "上下文资源"
        
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        print("退出上下文")
        await asyncio.sleep(1)

async def main():
    async with AsyncContextManager() as resource:
        print(f"使用资源: {resource}")

asyncio.run(main())

17. 异步迭代器与async for

可以在async for循环中使用的对象,支持异步迭代。

python 复制代码
import asyncio

class AsyncIterator:
    def __init__(self, limit):
        self.limit = limit
        self.counter = 0
        
    def __aiter__(self):
        return self
        
    async def __anext__(self):
        if self.counter < self.limit:
            self.counter += 1
            await asyncio.sleep(0.5)
            return self.counter
        else:
            raise StopAsyncIteration

async def main():
    async for number in AsyncIterator(5):
        print(number)  # 依次输出1到5

asyncio.run(main())

18. 异步生成器

使用async defyield定义的函数,能异步生成一系列值。

python 复制代码
import asyncio

async def async_generator(start, end):
    for i in range(start, end):
        await asyncio.sleep(0.1)  # 可以在yield前使用await
        yield i
        print(f"生成了 {i} 后继续执行")

async def main():
    # 使用async for迭代
    async for i in async_generator(1, 4):
        print(f"收到值: {i}")

asyncio.run(main())

19. 取消与超时处理

控制和取消长时间运行的异步操作,避免资源浪费。

python 复制代码
import asyncio

async def cancelable_operation():
    try:
        print("开始长时间操作")
        await asyncio.sleep(10)
        print("操作完成")  # 如果被取消,不会执行到这里
    except asyncio.CancelledError:
        print("操作被取消")
        raise  # 重新抛出异常,让调用者知道发生了取消

async def main():
    # 创建任务
    task = asyncio.create_task(cancelable_operation())
    
    # 等待1秒
    await asyncio.sleep(1)
    
    # 取消任务
    task.cancel()
    
    try:
        await task
    except asyncio.CancelledError:
        print("主函数:任务已取消")

asyncio.run(main())

20. 异步上下文变量 (contextvars)

在异步代码中安全地存储和访问上下文相关的状态,解决异步中的"全局变量"问题。

python 复制代码
import asyncio
import contextvars

# 创建上下文变量
request_id = contextvars.ContextVar('request_id', default=None)

async def process_request():
    # 获取当前请求ID
    current_id = request_id.get()
    print(f"处理请求 {current_id}")

async def handle_request(id):
    # 设置此协程上下文中的请求ID
    token = request_id.set(id)
    try:
        await process_request()
    finally:
        # 恢复之前的值
        request_id.reset(token)

async def main():
    # 并发处理多个请求,每个请求有自己的ID
    await asyncio.gather(
        handle_request("req-1"),
        handle_request("req-2"),
        handle_request("req-3")
    )

asyncio.run(main())

五、实际应用层

21. 异步网络I/O

使用异步方式进行网络请求,比传统方式更高效。

python 复制代码
import asyncio
import httpx

async def fetch_url(url):
    async with httpx.AsyncClient() as client:
        response = await client.get(url)
        return response.text

async def main():
    urls = [
        "https://example.com",
        "https://python.org",
        "https://github.com"
    ]
    
    # 并发获取多个URL
    tasks = [fetch_url(url) for url in urls]
    results = await asyncio.gather(*tasks)
    
    for url, html in zip(urls, results):
        print(f"{url}: 获取了 {len(html)} 字节")

# asyncio.run(main())

22. 异步文件I/O

使用aiofiles库进行非阻塞文件操作。

python 复制代码
import asyncio
import aiofiles

async def read_file(filename):
    async with aiofiles.open(filename, mode='r') as f:
        contents = await f.read()
        return contents

async def write_file(filename, content):
    async with aiofiles.open(filename, mode='w') as f:
        await f.write(content)

async def main():
    # 写入文件
    await write_file('test.txt', '这是异步写入的内容')
    
    # 读取文件
    content = await read_file('test.txt')
    print(f"读取到内容: {content}")

# asyncio.run(main())

23. 异步数据库操作

使用异步数据库驱动,提高数据库操作效率。

python 复制代码
import asyncio
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import declarative_base, sessionmaker
from sqlalchemy import Column, Integer, String, select

# 定义模型
Base = declarative_base()

class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    name = Column(String)
    email = Column(String)

async def example_db_operations():
    # 创建异步引擎
    engine = create_async_engine(
        "sqlite+aiosqlite:///test.db", 
        echo=True
    )
    
    # 创建表
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
    
    # 创建会话
    async_session = sessionmaker(
        engine, class_=AsyncSession, expire_on_commit=False
    )
    
    # 插入数据
    async with async_session() as session:
        user = User(name="John Doe", email="john@example.com")
        session.add(user)
        await session.commit()
    
    # 查询数据
    async with async_session() as session:
        result = await session.execute(select(User))
        users = result.scalars().all()
        for user in users:
            print(f"用户: {user.name}, 邮箱: {user.email}")
    
    # 关闭引擎
    await engine.dispose()

# asyncio.run(example_db_operations())

24. 异步Web服务器

使用现代异步Web框架创建高性能服务。

python 复制代码
from fastapi import FastAPI, BackgroundTasks
import asyncio
import uvicorn

app = FastAPI()

# 后台任务
async def process_data(data: str):
    await asyncio.sleep(2)  # 模拟长时间处理
    print(f"处理完成: {data}")

@app.get("/")
async def root():
    return {"message": "Hello World"}

@app.post("/items/")
async def create_item(item: dict, background_tasks: BackgroundTasks):
    # 添加后台任务异步处理
    background_tasks.add_task(process_data, item.get("data", ""))
    return {"status": "accepted", "message": "处理中"}

# 启动命令:uvicorn example:app --reload

25. 异步与多进程结合

将CPU密集型任务放在单独进程,同时用asyncio处理I/O操作,取长补短。

python 复制代码
import asyncio
import concurrent.futures
import time
import math

# CPU密集型任务
def cpu_bound_task(n):
    print(f"计算第 {n} 个素数")
    start = time.time()
    # 计算第n个素数
    count = 0
    num = 2
    while count < n:
        if all(num % i != 0 for i in range(2, int(math.sqrt(num)) + 1)):
            count += 1
        num += 1
    result = num - 1
    print(f"第 {n} 个素数是 {result},耗时 {time.time() - start:.2f} 秒")
    return result

# I/O密集型任务
async def io_bound_task(n):
    print(f"I/O任务 {n} 开始")
    await asyncio.sleep(1)  # 模拟I/O操作
    print(f"I/O任务 {n} 完成")
    return f"I/O结果 {n}"

async def main():
    # 创建进程池
    with concurrent.futures.ProcessPoolExecutor() as pool:
        # 在进程池中执行CPU密集型任务
        loop = asyncio.get_running_loop()
        cpu_tasks = [
            loop.run_in_executor(pool, cpu_bound_task, i*1000)
            for i in range(1, 5)
        ]
        
        # 同时处理I/O密集型任务
        io_tasks = [io_bound_task(i) for i in range(1, 5)]
        
        # 并发执行所有任务
        all_results = await asyncio.gather(
            *cpu_tasks,
            *io_tasks
        )
        
        print(f"所有结果: {all_results}")

# asyncio.run(main())

这就是Python异步编程的核心知识。掌握这些概念后,你就能写出既高效又清晰的异步代码,充分发挥Python的性能潜力。关键是理解事件循环、协程和任务之间的关系,以及何时该用异步、何时不该用。

记住,异步编程主要适合I/O密集型任务,对于CPU密集型任务,多进程通常是更好的选择。

相关推荐
用户9904501778009几秒前
JeecgFlow之Camunda开发脚手架介绍,让天下没有难用的工作流
后端
敖行客 Allthinker1 分钟前
Go 语言中 panic 和 recover 的代价:性能与设计的权衡
开发语言·后端·golang
WenGyyyL4 分钟前
使用OpenCV和MediaPipe库——驼背检测(姿态监控)
人工智能·python·opencv·算法·计算机视觉·numpy
蹦蹦跳跳真可爱58917 分钟前
Python----数据分析(Matplotlib四:Figure的用法,创建Figure对象,常用的Figure对象的方法)
python·数据分析·matplotlib
小杨4041 小时前
python入门系列六(文件操作)
人工智能·python·pycharm
谦行1 小时前
前端视角 Java Web 入门手册 4.4:Web 开发基础—— Listener
java·后端
xiaozaq1 小时前
在Eclipse中安装Lombok插件
java·python·eclipse
云空2 小时前
《FastRTC:开启实时音视频应用开发新时代》
python·实时音视频
非优秀程序员2 小时前
使用Python给自己网站生成llms.txt
人工智能·后端·架构
尘鹄2 小时前
一文讲懂Go语言如何使用配置文件连接数据库
开发语言·数据库·后端·golang