本篇文章主要内容:
- 协程的基本概念和Python协程语法
- Python 协程常用的 API
- 协程适用场景
1 基本概念
异步编程允许程序在等待 I/O 操作完成时继续执行其它任务,而不是被阻塞。异步编程是Python处理并发操作的强大方式,特别适合I/O密集型任务。Python的异步编程主要基于以下概念:
- 协程(Coroutine):可以暂停执行并稍后恢复的函数
- 事件循环(Event Loop): 管理和执行所有异步任务的核心调度器
- 任务(Task): 协程的包装器,使协程可以被调度执行
- Awaitable对象 : 可以在
await
表达式中使用的对象 - Future对象: 表示尚未完成的计算结果
1.1 异步函数定义
使用async def
关键字定义一个异步函数:
python
async def my_async_function():
print("Start of the function")
await some_async_operation()
print("End of the function")
1.2 await表达式
在异步函数中,使用await
关键字等待另一个异步操作完成:
python
async def fetch_data():
print("Fetching data...")
await asyncio.sleep(1) # 模拟一个耗时操作
print("Data fetched!")
1.3 运行协程
要运行一个协程,需要使用asyncio.run()
函数:
python
import asyncio
async def main():
await fetch_data()
asyncio.run(main())
1.4 并发执行多个协程
可以使用asyncio.gather()
来并发执行多个协程:
python
async def task1():
print("Task 1 started")
await asyncio.sleep(2)
print("Task 1 completed")
async def task2():
print("Task 2 started")
await asyncio.sleep(1)
print("Task 2 completed")
async def main():
await asyncio.gather(task1(), task2())
asyncio.run(main())
生成器协程与原生协程
在Python 3.7及以上版本中,推荐使用async def
定义的原生协程。旧版本中也可以使用生成器协程,但需要使用asyncio.coroutine
装饰器:
python
import asyncio
@asyncio.coroutine
def old_style_coroutine():
print("Old style coroutine started")
yield from asyncio.sleep(1)
print("Old style coroutine completed")
async def main():
await old_style_coroutine()
asyncio.run(main())
2. Python 协程常用的 API
2.1 asyncio.run()
简化了运行协程的方式,自动管理事件循环。
示例:
python
import asyncio
async def main():
await asyncio.sleep(1)
print("Hello, asyncio!")
asyncio.run(main())
2.2 asyncio.sleep()
用于暂停协程执行,模拟I/O操作。
示例:
python
async def delay_print(message):
await asyncio.sleep(1)
print(message)
async def main():
await asyncio.gather(
delay_print("Hello"),
delay_print("World")
)
asyncio.run(main())
2.3 asyncio.gather()
将多个协程组合为一个,同时运行。
示例:
python
async def func1():
print("Function 1 started")
await asyncio.sleep(2)
print("Function 1 done")
async def func2():
print("Function 2 started")
await asyncio.sleep(1)
print("Function 2 done")
async def main():
await asyncio.gather(func1(), func2())
asyncio.run(main())
2.4 asyncio.EventLoop
管理协程的执行循环。常用方法包括:
loop.run_forever()
: 开始事件循环,直到loop.stop()
被调用。loop.run_until_complete()
: 执行直到协程完成。
示例:
python
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
事件循环通常用于网络编程。
2.5 asyncio.Task
用于管理协程任务。
示例:
python
async def task_func():
print("Task is running")
async def main():
task = asyncio.create_task(task_func())
await task
asyncio.run(main())
2.6 asyncio.Queue
在协程间安全地传递数据。
示例:
python
async def producer(queue):
await queue.put("Hello")
await queue.put("World")
await queue.put(None)
async def consumer(queue):
while True:
item = await queue.get()
if item is None:
break
print(item)
async def main():
queue = asyncio.Queue()
await asyncio.gather(
producer(queue),
consumer(queue)
)
asyncio.run(main())
2.7 asyncio.Event
用于协程间的同步。
示例:
python
import asyncio
import time
async def waiter(event, name):
print(f"等待者 {name} 开始等待事件")
start_time = time.time()
await event.wait() # 等待事件被设置
end_time = time.time()
print(f"等待者 {name} 检测到事件发生,等待时间: {end_time - start_time:.2f}秒")
async def event_setter(event, delay):
print(f"事件设置者开始工作,将在 {delay} 秒后设置事件")
await asyncio.sleep(delay) # 模拟一些工作
print("事件设置者正在设置事件...")
event.set() # 设置事件,唤醒所有等待者
print("事件已设置")
async def main():
# 创建一个事件对象
event = asyncio.Event()
# 创建多个等待者任务
waiters = [
waiter(event, "A"),
waiter(event, "B"),
waiter(event, "C")
]
# 创建事件设置者任务
setter = event_setter(event, 3)
# 同时运行所有任务
await asyncio.gather(*waiters, setter)
if __name__ == "__main__":
print("程序开始运行")
asyncio.run(main())
print("程序运行结束")

实际应用场景:
- 资源初始化:多个任务等待某个资源(如数据库连接)初始化完成;
- 任务协调:多个工作线程等待某个条件满足后开始工作;
- 事件通知:实现发布-订阅模式的基础。
2.8 asyncio.Condition
asyncio.Condtion()
常被用来需要等待某个资源条件是否满足的情况。
下面使用协程实现了一个生产者-消费者模式。
python
import asyncio
import random
import time
# 共享资源
shared_resource = []
MAX_ITEMS = 5
async def producer(condition, name):
"""生产者协程:向共享资源添加数据"""
while True:
# 模拟生产数据的时间
await asyncio.sleep(random.uniform(0.5, 1.5))
async with condition:
# 如果资源已满,等待消费者消费
while len(shared_resource) >= MAX_ITEMS:
print(f"生产者 {name}: 资源已满,等待消费者...")
await condition.wait()
# 生产数据
item = f"产品-{name}-{time.time()}"
shared_resource.append(item)
print(f"生产者 {name} 生产了: {item}")
print(f"当前资源数量: {len(shared_resource)}")
# 通知所有等待的消费者
condition.notify_all()
async def consumer(condition, name):
"""消费者协程:从共享资源消费数据"""
while True:
# 模拟消费数据的时间
await asyncio.sleep(random.uniform(0.5, 1.5))
async with condition:
# 如果资源为空,等待生产者生产
while len(shared_resource) == 0:
print(f"消费者 {name}: 资源为空,等待生产者...")
await condition.wait()
# 消费数据
item = shared_resource.pop(0)
print(f"消费者 {name} 消费了: {item}")
print(f"当前资源数量: {len(shared_resource)}")
# 通知所有等待的生产者
condition.notify_all()
async def main():
# 创建条件对象
condition = asyncio.Condition()
# 创建生产者和消费者任务
producers = [
producer(condition, f"P{i}") for i in range(2)
]
consumers = [
consumer(condition, f"C{i}") for i in range(3)
]
# 运行所有任务
await asyncio.gather(*producers, *consumers)
if __name__ == "__main__":
print("生产者-消费者模型开始运行")
try:
asyncio.run(main())
except KeyboardInterrupt:
print("\n程序被用户中断")
执行结果:

你会发现 asyncio.Condtion()
对象的 API·和 threading.Condition()
很相似,这里就不再过多介绍了。
下面是这个程序的时序图,有助于你理解这个程序的逻辑。

使用场景:
- 生产者-消费者模式:协调生产者和消费者的工作,控制资源的使用和释放。
- 资源池管理:管理数据库连接池,控制线程池大小。
- 任务调度:实现任务队列,控制并发任务数量。
- 状态同步:等待特定状态变化,实现复杂的同步逻辑。
- 事件驱动系统:实现发布-订阅模式,处理异步事件通知。
与 asyncio.Event()
的区别:
asyncio.Event()
只有简单的标志位、只有设置和未设置两种状态、适合简单的同步场景。
asyncio.Condition()
可以等待特定条件、支持选择性通知,比较适合复杂的同步场景。
2.9 第三方库
- aiohttp: 异步HTTP客户端和服务器。
- aiofiles: 异步文件处理。
- asyncio_redis : 异步Redis客户端。
示例(aiohttp):
python
import aiohttp
import asyncio
async def fetch(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
async with aiohttp.ClientSession() as session:
html = await fetch(session, 'https://example.com')
print(html)
asyncio.run(main())
3. Python 协程的实现原理
3.1 协程和线程的区别

3.2 Python 协程的底层工作过程
Python协程的底层实现主要基于生成器和 yield 语句的扩展,以及通过事件循环来管理和调度这些协程,后来又引入了async/await语法。
下面的时序图展示了Python协程的底层工作过程,从协程创建到执行、挂起、恢复,直到完成的整个生命周期。
以下是图中关键步骤的详细解释:
- 创建阶段 :应用程序创建事件循环和协程对象。
- 使用
async def
定义协程函数,创建事件循环 - 协程在定义时并不执行,只是创建了一个协程对象
- 使用
- 初始执行 :
- 协程首次被事件循环执行时,会创建栈帧对象保存执行状态
- 协程内部代码开始执行,直到遇到第一个
await
点
- 挂起与调度 :
- 当遇到
await
表达式时,协程会暂停执行并让出控制权 - 底层通过
yield
机制实现,协程状态从GEN_RUNNING
变为GEN_SUSPENDED
- 被挂起的协程的栈帧会被保存,包括局部变量、指令指针等执行状态
- 当遇到
- I/O操作与回调 :
- 事件循环注册I/O回调,继续执行其他就绪的任务
- 当I/O操作完成时,回调被触发,协程被标记为可恢复执行。 在 Python异步编程中,I/O操作完成通知事件循环的机制主要基于操作系统的I/O多路复用功能。I/O 多路复用机制,如 Linux 的 epoll、Windows 的 IOCP,这些机制允许一个进程监控多个文件描述符,等待它们变为可读或可写的状态。
- 恢复执行 :
- 事件循环通过
.send()
方法恢复协程执行,传递I/O操作的结果 - 协程从上次挂起的位置继续执行,状态从
GEN_SUSPENDED
变为GEN_RUNNING
- 事件循环通过
- 完成或再次挂起 :
- 如果协程执行完成,它会将结果返回给事件循环,状态变为
GEN_COMPLETED
- 如果遇到新的
await
点,协程会再次挂起,重复上述过程
- 如果协程执行完成,它会将结果返回给事件循环,状态变为