想用好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 def
和yield
定义的函数,能异步生成一系列值。
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密集型任务,多进程通常是更好的选择。