python进程和线程(三、主要讲解协程)
一、协程是什么
协程(Coroutine) 是一种用户态、轻量级、可暂停 / 可恢复的函数,由程序主动调度(协作式),而非操作系统抢占式调度。
- 进程:资源分配单位,开销最大。
- 线程:CPU 调度单位,内核抢占,开销中等。
- 协程:用户态协作,单线程内并发,开销极小(KB 级栈)。
核心优势:
- 高并发:单机可跑数十万协程(线程 / 进程仅数千)。
- 低开销:切换仅保存 / 恢复栈帧,无内核态切换。
- 易控:主动
await让出 CPU,无线程安全问题。
重点:协程不是计算机提供的,是由程序员人为创建的。协程也被秒杀为微线程, 是一种用户态内的上下文切换技术。简而言之,其实就是通过一个线程实现代码块相互切换执行。
二、python实现协程的几种方式
在 Python 中,实现协程主要有以下几种方式,它们代表了 Python 异步编程的发展历程。
🧬 1. greenlet 库
greenlet 是一个第三方库,它提供了一种轻量级的协程实现,允许你在不同的“绿色线程”之间手动切换执行。它并非 Python 原生的异步编程方式,而是一种协作式的多任务处理。- 特点:需要手动调用
switch()方法来控制协程的切换。 - 使用:首先需要安装
pip install greenlet。
from greenlet import greenlet
def func1():
print(1)
gr2.switch() # 切换到 gr2
print(2)
def func2():
print(3)
gr1.switch() # 切换到 gr1
print(4)
gr1 = greenlet(func1)
gr2 = greenlet(func2)
gr1.switch() # 启动 gr1
目前在pycharm中安装greenlet库没有成功。
🔄 2. yield 关键字 (生成器)
在
async/await 语法出现之前,Python 的生成器(generator)被用来实现协程的效果。通过 yield 关键字,函数可以暂停执行并保存当前状态,等待下一次被唤醒。- 特点:利用生成器的暂停和恢复能力,是早期实现协程的一种方式。
def func1():
yield 1
yield from func2()
yield 2
def func2():
yield 3
yield 4
f1 = func1()
for item in f1:
print(item) # 输出: 1, 3, 4, 2
⏳ 3. asyncio 装饰器 (Python 3.4+)
Python 3.4 引入了标准库
asyncio,并使用 @asyncio.coroutine 装饰器来声明一个协程函数。这种方式结合了生成器的 yield from 语法来处理异步操作。- 特点:这是 Python 官方对异步编程的早期支持,但语法相对繁琐,现已不推荐使用。
- 注意:在 Python 3.8 之后的版本中,
@asyncio.coroutine装饰器和yield from语法已被弃用。
import asyncio
@asyncio.coroutine
def func1():
print(1)
yield from asyncio.sleep(1)
print(2)
# 获取事件循环并运行
loop = asyncio.get_event_loop()
loop.run_until_complete(func1())
loop.close()
我的pycharm使用的是python 3.12,无法运行上述已过时的代码。
✨ 4. async 和 await 关键字 (Python 3.5+, 推荐)
这是 Python 3.5 之后引入的现代语法,也是目前最推荐的实现方式。它使用
async def 来定义协程函数,使用 await 关键字来等待一个异步操作完成。这种方式让异步代码的结构和可读性都更像同步代码。- 特点:语法清晰、易于理解,是 Python 异步编程的标准。
- 运行:在 Python 3.7+ 中,可以使用
asyncio.run()来简化运行协程的步骤。
核心语法
async def:定义协程函数await:暂停协程,等待 IO 完成asyncio.run():运行主协程(Python 3.7+)asyncio.gather():并发运行多个协程
import asyncio
# 定义协程函数
async def task(name, delay):
print(f"任务{name} 开始,等待{delay}秒")
await asyncio.sleep(delay) # 模拟IO阻塞,自动切换其他协程
print(f"任务{name} 完成")
async def main():
# 并发执行多个协程
await asyncio.gather(
task("A", 2),
task("B", 1),
task("C", 1.5)
)
# 运行主协程
if __name__ == '__main__':
asyncio.run(main())

针对上面的核心语法并结合代码实例进行更一步的详细讲解:
4.1. 定义协程函数
用
async def 声明,调用后得到协程对象(不自动执行)Python。import asyncio
async def hello():
print("Hello")
await asyncio.sleep(1) # 异步等待,不阻塞线程
print("World")
print(hello()) # <coroutine object hello at ...>
4.2. 运行协程:事件循环
asyncio.run() 是入口,自动创建事件循环并运行协程Python。asyncio.run(hello())
# 输出:Hello → (1秒后) → World
4.3. await:暂停与恢复
await只能在async def内部使用。- 遇到
await时,协程主动挂起,让出 CPU 给其他任务;等待完成后恢复执行Python。
🤔 await 能用在什么地方?
await 关键字的使用有两条铁律:-
- 它只能在用
async def定义的协程函数内部使用。 - 它后面必须跟一个可等待对象(Awaitable)。
- 它只能在用
什么是可等待对象?
可等待对象主要有以下三类:
-
- 协程对象 (Coroutine Object):由
async def定义的函数调用后返回的对象。 - Task 和 Future 对象:
asyncio库中用于调度和表示未来结果的对象。 - 实现了
__await__()方法的对象:任何类只要实现了这个方法并返回一个迭代器,其实例就可以被await。
- 协程对象 (Coroutine Object):由
4.4. asyncio.create_task ():创建任务
将协程封装为 Task,加入事件循环并发调度。
async def say_after(delay, msg):
await asyncio.sleep(delay)
print(msg)
async def main():
# 创建两个任务(立即调度,不阻塞)
task1 = asyncio.create_task(say_after(1, "A"))
task2 = asyncio.create_task(say_after(2, "B"))
await task1 # 等待任务1完成
await task2 # 等待任务2完成
asyncio.run(main())
# 输出:(1秒)A → (2秒)B (总耗时≈2秒,非3秒)
4.5. asyncio.gather ():批量并发
一键运行多个协程 / 任务,等待全部完成并收集结果Python。
async def main():
# 并发运行3个协程,等待全部完成
results = await asyncio.gather(
say_after(1, "X"),
say_after(2, "Y"),
say_after(3, "Z")
)
print("全部完成")
asyncio.run(main())
# 总耗时≈3秒,顺序输出X→Y→Z


浙公网安备 33010602011771号