1、协程是一种轻量级的并发机制,允许你在单个线程内模拟并发执行多个任务。协程非常适合用于 I/O 密集型任务,如网络请求、文件读写等,在等待 I/O 操作完成时,协程可以继续执行其他任务而不是阻塞。
-
生成器:
- 协程的基础是生成器(generator)。生成器是一种特殊的迭代器,它可以使用
yield
表达式暂停其执行,并在后续调用next()
方法时恢复执行。 - 生成器可以使用
yield
表达式返回一个值,并保存当前的状态,以便下次调用时可以从上次暂停的地方继续执行。
- 协程的基础是生成器(generator)。生成器是一种特殊的迭代器,它可以使用
-
异步编程:
- 协程可以用来编写异步代码,这样就可以在等待某些操作(如 I/O 操作)完成时执行其他任务。
- Python 3.5 引入了
asyncio
模块,提供了协程和异步编程的支持。
-
关键字
async
和await
:async def
用于定义一个协程函数。await
用于在协程函数内部等待另一个协程的完成。import asyncio # 定义一个协程函数 async def task(name, delay): print(f"{name} task starts.") await asyncio.sleep(delay) # 模拟一个异步操作 print(f"{name} task finishes.") async def main(): # 创建并启动两个任务 task1 = asyncio.create_task(task("Task 1", 2)) task2 = asyncio.create_task(task("Task 2", 1)) # 等待所有任务完成 # await task1 # await task2 await asyncio.gather(task1, task2) # 运行协程 asyncio.run(main())
2、gevent
是一个 Python 库,它提供了一种轻量级的并发机制,使用 greenlets(绿色线程)来实现协程
gevent 的特点
-
轻量级:
gevent
的 greenlets 是轻量级的,可以在单个线程内运行多个 greenlets。
-
非抢占性调度:
- greenlets 的调度是非抢占式的,这意味着一个 greenlet 只能在遇到
switch
方法时主动让出控制权。
- greenlets 的调度是非抢占式的,这意味着一个 greenlet 只能在遇到
-
共享内存:
- greenlets 共享相同的内存空间,这意味着它们可以直接访问相同的变量和数据结构。
-
适合 I/O 密集型任务:
gevent
非常适合处理 I/O 密集型任务,如网络请求、文件读写等。在等待 I/O 操作完成时,greenlet 可以继续执行其他任务。
-
异步编程:
gevent
提供了一种自然的方式来编写异步代码,使得代码更容易理解和维护。
-
自动猴子补丁:
gevent
提供了一个“猴子补丁”(monkey patching)功能,可以将 Python 的标准库中的阻塞调用替换为非阻塞的版本。
from gevent import monkey; monkey.patch_all() # 必须在导入其他库之前执行
import gevent
import requests
def fetch_url(url):
print(f"Fetching {url}...")
response = requests.get(url)
print(f"Fetched {url}: {len(response.text)} bytes")
def main():
urls = [
"https://www.example.com",
"https://www.example.org",
"https://www.example.net"
]
greenlets = [gevent.spawn(fetch_url, url) for url in urls]
gevent.joinall(greenlets)
if __name__ == "__main__":
start_time = time.time()
main()
end_time = time.time()
print(f"Total time taken: {end_time - start_time:.2f} seconds")
代码解释
-
Monkey Patching:
monkey.patch_all()
用于自动将标准库中的阻塞调用替换为 gevent 的非阻塞版本。这是为了让 gevent 能够接管标准库中的网络请求等 I/O 操作。
-
定义 greenlet 函数:
fetch_url
函数接受一个 URL 参数,并使用requests.get()
发送网络请求。
-
创建并启动 greenlets:
- 使用
gevent.spawn()
来创建并启动 greenlets。 gevent.joinall()
用于等待所有 greenlets 完成。
- 使用
-
性能测量:
- 使用
time.time()
来记录开始和结束时间,计算总的执行时间。
- 使用
gevent 与 asyncio 的区别
-
实现方式:
- gevent 由
gevent
库提供支持,使用gevent.greenlet.Greenlet
类。 - asyncio 是 Python 3.5+ 中的原生特性,使用
async/await
关键字。
- gevent 由
-
调度方式:
- gevent 的调度是由 gevent 库控制的。
- asyncio 的调度是由 Python 的异步运行时(如
asyncio
)控制的。
-
异步支持:
- gevent 需要使用 monkey patching 来使标准库中的 I/O 操作变为非阻塞。
- asyncio 通常与
asyncio
一起使用,提供了一套完整的异步编程模型