Python中的协程(Coroutine) 是一种轻量级的异步执行单元,主要用于解决IO密集型任务的性能问题。Python 3.5引入了 async/await 语法,使得协程变得简洁且易于使用。协程的核心是通过事件循环(Event Loop) 来调度任务,在等待外部操作(如网络请求、文件读写)时,自动切换到其他任务,从而提升程序的整体效率。


一、协程的基本概念

  1. 协程(Coroutine)

  2. 是一个暂停和恢复执行的函数,通过 async def 定义。
  3. 协程不会阻塞整个事件循环,而是通过 await 关键字主动让出控制权。
  4. 协程本身不会自动运行,需要通过事件循环(如 asyncio)来驱动。
  5. 事件循环(Event Loop)

  6. 是协程调度的核心,负责管理任务的执行顺序、IO事件的监听和处理。
  7. Python标准库 asyncio 提供了事件循环的实现。

二、协程的常见用法

1. 定义和运行协程
import asyncio

async def my_coroutine():
    print("Coroutine started")
    await asyncio.sleep(1)  # 模拟耗时操作(如IO)
    print("Coroutine finished")

# 运行协程
asyncio.run(my_coroutine())  # Python 3.7+ 推荐
2. 使用 asyncawait
  • async def:定义一个协程函数。
  • await:在协程内部调用另一个协程,当遇到 await 时,当前协程暂停,让出控制权。
  • async def fetch_data():
        print("Start fetching")
        await asyncio.sleep(2)  # 模拟网络请求
        return "Data"
    
    async def main():
        result = await fetch_data()  # 等待fetch_data完成
        print(result)  # 输出:Data
    
    asyncio.run(main())
    
    3. 并发执行多个协程

    使用 asyncio.gather() 并发运行多个协程:

    async def task1():
        await asyncio.sleep(1)
        return "Task1 Done"
    
    async def task2():
        await asyncio.sleep(2)
        return "Task2 Done"
    
    async def main():
        results = await asyncio.gather(task1(), task2())
        print(results)  # 输出:["Task1 Done", "Task2 Done"]
    
    asyncio.run(main())
    
    4. 异步迭代和上下文管理器
  • 异步生成器:通过 async for 迭代异步序列。
  • 异步上下文管理器:通过 async with 管理资源。
  • # 异步生成器示例
    async def async_gen():
        for i in range(3):
            await asyncio.sleep(1)
            yield i
    
    async def main():
        async for item in async_gen():
            print(item)  # 输出0, 1, 2
    
    # 异步上下文管理器示例(如打开文件)
    async with aiofiles.open("file.txt", mode="r") as f:
        content = await f.read()
    

    三、常用协程类库

    以下是Python中常用的协程相关库及典型用法:

    1. asyncio(标准库)

    Python内置的异步事件驱动框架,提供协程、事件循环、Future/Task等核心功能。

  • 核心组件
  • Event Loop:事件循环管理器(如 asyncio.get_event_loop())。
  • Task:将协程封装为任务,以便在事件循环中调度。
  • Future:表示异步操作的最终结果。
  • 典型用法
    async def hello():
        print("Hello")
        await asyncio.sleep(1)
        print("World")
    
    # 获取事件循环并运行
    loop = asyncio.get_event_loop()
    loop.run_until_complete(hello())  # 或 asyncio.run(hello())
    
  • 2. aiohttp

    基于 asyncio 的异步HTTP客户端和服务器库,适用于高性能Web爬虫或Web服务。

  • 客户端用法
    import aiohttp
    
    async def fetch():
        async with aiohttp.ClientSession() as session:
            async with session.get("https://api.example.com/data") as response:
                return await response.json()
    
    asyncio.run(fetch())
    
  • 服务器用法
    from aiohttp import web
    
    async def handle(request):
        return web.Response(text="Hello, Aiohttp!")
    
    app = web.Application()
    app.router.add_get("/", handle)
    web.run_app(app)
    
  • 3. asyncpg

    用于 PostgreSQL 的异步数据库驱动,适用于异步数据库操作。

    import asyncpg
    
    async def main():
        conn = await asyncpg.connect(user='user', password='password',
                                    database='db', host='127.0.0.1')
        values = await conn.fetch("SELECT * FROM my_table")
        await conn.close()
    
    asyncio.run(main())
    
    4. aiofiles

    异步文件操作库,替代 open() 函数,适用于大文件处理或需要异步读写的场景。

    import aiofiles
    
    async def read_file():
        async with aiofiles.open("large_file.txt", mode="r") as f:
            content = await f.read()
            print(content)
    
    5. gevent

    基于 greenlet 的协程库,通过协程模拟多线程,支持同步代码异步化(非async/await语法)。

    import gevent
    from gevent import monkey; monkey.patch_all()  # 打补丁
    
    def task(name, n):
        for i in range(n):
            print(f"{name}: {i}")
            gevent.sleep(0.1)
    
    gevent.joinall([
        gevent.spawn(task, "A", 3),
        gevent.spawn(task, "B", 5)
    ])
    

    四、协程 vs 线程/进程

    特性 协程 多线程 多进程
    资源消耗 极低(共享线程/进程资源) 中(线程资源) 高(进程资源)
    切换方式 用户态协作式切换 内核级抢占式切换 内核级抢占式切换
    适合场景 IO密集型(如网络请求、文件) 轻量级并发(如小计算任务) CPU密集型(如科学计算)
    GIL影响 在CPython中受GIL限制 受GIL限制 不受GIL限制(每个进程独立)

    五、最佳实践与注意事项

    1. 避免阻塞操作:协程内部应避免长时间阻塞(如 time.sleep()),改用 asyncio.sleep()
    2. 合理使用 await:确保在协程中正确使用 await,否则代码不会异步执行。
    3. 错误处理:使用 try/except 捕获异步操作的异常。
    4. 调试:协程的调试较复杂,建议使用 asyncio.debug 或专用调试工具。
    5. 库的兼容性:非异步库需要通过 loop.run_in_executor() 转换为异步操作。

    六、典型应用场景

    1. Web爬虫:并发请求多个网页,异步处理响应。
    2. 实时数据处理:如股票行情、物联网传感器数据流。
    3. 高性能服务器:构建异步HTTP服务器或WebSocket服务。
    4. 游戏或模拟器:需要处理大量并发事件的场景。

    七、扩展学习资源

    1. 官方文档
    2. asyncio:https://docs.python.org/3/library/asyncio.html
    3. aiohttp:https://aiohttp.readthedocs.io/
    4. 书籍
    5. 《Fluent Python》第22章(协程和事件循环)。
    6. 《Python异步编程实战》(异步IO、协程及框架应用)。

    通过合理使用协程和相关库,可以显著提升Python在IO密集型任务中的性能和响应能力!

    作者:高效匠人

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python协程深度解析

    发表回复