Python协程的作用

过分揣测别人的想法,就会失去自己的立场。大家好,当代软件开发领域中,异步编程已成为一种不可或缺的技术,用于处理大规模数据处理、高并发网络请求、实时通信等应用场景。而Python协程(Coroutine)作为一种高效的异步编程模型,引起了广泛的关注和应用。通过利用Python协程,开发者可以以更加简洁、可读性更高的方式编写异步代码,实现并发执行和非阻塞IO操作,从而提高程序的性能和响应能力。

一、协程介绍

协程(Coroutines)是一种轻量级的并发编程方式,可用于在单线程内实现并发和异步操作。Python 提供了协程的支持,通过 asyncio 模块和 async/await 关键字,可以编写异步代码并实现高效的非阻塞 IO 操作。

Python 协程相对于多线程具有以下几个优势:

  • 轻量级:协程是在单个线程内执行的,因此相比于多线程,协程的创建和切换开销较小,占用的系统资源更少。

  • 高效的并发:协程利用异步非阻塞的方式实现并发操作,可以在等待 IO 操作时挂起,让出 CPU 的执行权,从而充分利用 CPU 的处理能力。相比于多线程,协程在 IO 密集型任务中通常表现更好。

  • 简化的同步编程:使用协程和 async/await 关键字,可以编写类似于同步代码的异步代码。协程在编写和理解上更加直观和简单,避免了传统多线程编程中的锁和线程同步等问题。

  • 避免竞态条件:由于协程在单线程内运行,不存在多线程的竞态条件(Race Condition)问题。协程的执行是按顺序逐个执行的,不需要额外的同步机制来保证数据的一致性。

  • 可扩展性:协程模型可以方便地实现高层级的并发和并行操作。通过协程的组合和调度,可以构建复杂的并发模式,如并发任务的并行执行、协程间的消息传递等。

应用场景:

  • 异步网络编程:协程在网络编程中非常有用,特别是在处理大量并发连接的情况下。通过使用协程,可以实现高性能的异步网络服务器和客户端,处理并发的网络请求、响应和数据传输。

  • 异步 IO 操作:协程适用于处理各种异步 IO 操作,如文件读写、数据库查询、网络请求等。通过使用协程,可以避免阻塞主线程,提高程序的吞吐量和响应能力。

  • Web 开发框架:许多 Python Web 框架(如Tornado、Sanic、FastAPI等)使用协程作为并发处理请求的方式。这些框架利用协程的特性处理大量的并发请求,提供高性能的 Web 服务。

  • 数据爬取和抓取:协程在数据爬取和抓取任务中非常常见。通过使用协程,可以并发地发送请求、解析响应和提取数据,从而加快数据获取的速度。

  • 并发任务调度:协程可以用于并发任务的调度和执行。通过构建协程任务队列,并使用协程调度器进行任务的调度和执行,可以实现高效的并发任务处理。

  • 高性能计算:虽然协程主要用于处理 IO 密集型任务,但在某些情况下,也可以用于并发执行计算密集型任务。通过合理的任务调度和利用协程的非阻塞特性,可以充分利用 CPU 资源,提高计算任务的执行效率。

Python 协程相对于多线程更加轻量且高效,适用于处理 IO 密集型任务和异步编程场景。它简化了异步编程的复杂性,并提供了一种更加直观和简单的方式来实现并发操作。然而,协程也有一些局限性,例如无法利用多核 CPU 的并行处理能力,因此在 CPU 密集型任务中可能不如多线程效果好。在实际应用中,需要根据具体的需求和场景选择适合的并发模型。

二、asyncawait 关键字

asyncawait 是 Python 3.5+ 引入的关键字,用于定义和管理协程(Coroutines)。

1、async 关键字

async 关键字用于修饰函数 ,将其标记为协程函数(coroutine function)。协程函数在调用时返回一个协程对象,并且可以包含 await 表达式。

协程函数的执行过程中,可以使用 await 等待其他协程对象的执行结果,同时挂起自身的执行,并将控制权交给事件循环(Event Loop)。

在协程函数内部,可以执行异步操作,如异步 IO、 网络请求等,而无需阻塞整个程序的执行。

python 复制代码
async def my_coroutine():
    # 协程函数的逻辑
    await some_async_operation()
    # 其他操作

2、await 关键字

  • await 关键字用于等待一个协程对象的执行结果,并暂时挂起当前协程的执行,直到该协程执行完成并返回结果。
  • 在协程函数内部使用 await 关键字时,协程会将控制权交给事件循环,以便在等待期间执行其他协程或异步操作。
  • await 表达式通常用于异步操作的调用,如异步 IO 操作、网络请求等,以便在等待期间不阻塞程序的其他部分。
python 复制代码
async def my_coroutine():
    # 协程函数的逻辑
    result = await some_async_operation()
    # 使用结果进行其他操作

使用 asyncawait 关键字可以轻松地编写异步代码,实现协程的挂起和恢复,并在等待异步操作时不阻塞整个程序的执行。这种方式使得编写异步程序更加直观和简洁,类似于编写同步代码的方式。同时,通过事件循环的调度,协程可以高效地处理并发任务和异步操作,提高程序的性能和响应能力。

三、协程对象

协程对象是使用 async 关键字定义的函数的返回结果。它代表一个暂停执行的函数,可以通过 await 等待其执行完成,并获得其返回值。

1、创建协程对象

  • 协程对象是通过调用使用 async 关键字定义的协程函数来创建的。协程函数在调用时不会立即执行函数体内的代码,而是返回一个协程对象。
  • 协程对象可以像普通函数一样被调用,但是它的执行需要通过事件循环(Event Loop)的调度来实现。
python 复制代码
import asyncio

async def my_coroutine():
    # 协程逻辑
    await asyncio.sleep(1)
    return "Coroutine finished"

coro = my_coroutine()  # 创建协程对象

2、等待协程对象的执行

  • 协程对象可以被等待和执行,以获取其执行结果。这通常通过在其他协程或异步函数内使用 await 关键字来实现。
  • 在等待协程对象时,当前的协程会暂时挂起,让出事件循环的控制权,等待协程对象的执行完成,并获得其返回值。
python 复制代码
import asyncio

async def my_coroutine():
    # 协程逻辑
    await asyncio.sleep(1)
    return "Coroutine finished"

async def main():
    result = await my_coroutine()  # 等待协程对象的执行
    print(result)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

3、协程对象的状态

  • 协程对象有三种状态:等待状态(awaitable)、运行状态(running)和完成状态(done)。
  • 初始时,协程对象处于等待状态,表示它可以被等待执行。当协程对象被事件循环调度执行时,进入运行状态。最终,当协程对象执行完成时,进入完成状态。
python 复制代码
import asyncio

async def my_coroutine():
    # 协程逻辑
    await asyncio.sleep(1)
    return "Coroutine finished"

coro = my_coroutine()  # 创建协程对象
print(coro)  # <coroutine object my_coroutine at 0x7f1e5c0b5d60>
print(coro.cr_running)  # False,表示协程对象不在运行状态
print(coro.cr_await)  # None,表示协程对象没有被等待

三、事件循环

事件循环(Event Loop)是协程编程中的核心概念,用于调度和执行协程对象。它负责协调协程的执行顺序,处理协程的挂起和恢复,并管理异步操作的完成事件。

1、创建事件循环

  • 在 Python 中,可以通过 asyncio 模块的 get_event_loop() 方法来获取默认的事件循环对象。
  • 也可以使用 asyncio 模块的 new_event_loop() 方法来创建新的事件循环对象。
python 复制代码
import asyncio

loop = asyncio.get_event_loop()  # 获取默认的事件循环对象
# 或者
loop = asyncio.new_event_loop()  # 创建新的事件循环对象

2、注册协程对象

  • 使用事件循环的 create_task() 方法可以将协程对象注册到事件循环中,以便事件循环调度其执行。
  • 注册后的协程对象会在事件循环的调度下按照顺序执行。
python 复制代码
import asyncio

async def my_coroutine():
    # 协程逻辑
    await asyncio.sleep(1)
    print("Coroutine finished")

loop = asyncio.get_event_loop()
task = loop.create_task(my_coroutine())  # 注册协程对象到事件循环

3、运行事件循环

  • 通过调用事件循环的 run_until_complete() 方法,并传入需要运行的协程对象,可以启动事件循环并运行协程直到其完成。
  • 在运行过程中,事件循环会根据协程的状态和事件的发生情况,决定哪个协程继续执行、哪个协程挂起等。
python 复制代码
import asyncio

async def my_coroutine():
    # 协程逻辑
    await asyncio.sleep(1)
    print("Coroutine finished")

async def main():
    loop = asyncio.get_event_loop()
    task = loop.create_task(my_coroutine())  # 注册协程对象到事件循环
    await task

asyncio.run(main())  # 运行事件循环,直到协程完成

4、控制事件循环

  • 事件循环提供了一些方法来控制其行为,如 stop() 方法用于停止事件循环的运行,is_running() 方法用于检查事件循环是否在运行中等。
  • 可以使用 run_forever() 方法来启动事件循环,并使其一直运行,直到手动停止。
python 复制代码
import asyncio

async def my_coroutine():
    # 协程逻辑
    await asyncio.sleep(1)
    print("Coroutine finished")

async def main():
    loop = asyncio.get_event_loop()
    task = loop.create_task(my_coroutine())  # 注册协程对象到事件循环
    await task

    loop.stop()  # 停止事件循环

loop = asyncio.get_event_loop()
loop.run_until_complete(main())  # 运行事件循环,直到协程完成

四、异步操作和挂起点

异步操作和挂起点是协程编程中的关键概念,用于处理并发任务和实现非阻塞的异步操作。

1、异步操作

  • 异步操作是指可以在后台进行而不会阻塞程序执行的操作。这些操作通常涉及 IO 操作、网络请求、数据库查询等耗时的任务。
  • 在传统的同步编程中,这些操作会阻塞程序的执行,直到操作完成才能继续执行后续代码。而在异步编程中,这些操作可以在后台进行,程序可以继续执行其他任务。
  • 异步操作通常是通过使用异步库、框架或语言提供的异步函数来实现。

2、挂起点(Suspend Point)

  • 挂起点是协程中的一个特殊位置,在该位置协程可以暂时挂起自身的执行,让出事件循环的控制权,等待某个条件满足或异步操作完成后再继续执行。
  • 挂起点通常通过使用 await 表达式来实现,它用于等待一个异步操作的完成,并将控制权交给事件循环。
  • 在挂起点之前的代码会被执行,但在挂起点之后的代码在等待期间不会执行,直到挂起点恢复执行。
python 复制代码
import asyncio

async def my_coroutine():
    print("Before await")
    await some_async_operation()  # 挂起点
    print("After await")

async def some_async_operation():
    await asyncio.sleep(1)  # 模拟异步操作

asyncio.run(my_coroutine())

3、挂起和恢复

  • 当协程遇到挂起点时,它会暂停执行,并将控制权交给事件循环,以便在等待期间执行其他协程或异步操作。
  • 一旦挂起点等待的条件满足或异步操作完成,协程会从挂起点恢复执行,并继续执行挂起点之后的代码。
  • 挂起和恢复的过程是通过事件循环的调度来实现的,事件循环会根据协程的状态和事件的发生情况,决定哪个协程继续执行、哪个协程挂起等。
python 复制代码
import asyncio

async def my_coroutine():
    print("Before await")
    await some_async_operation()  # 挂起点
    print("After await")

async def some_async_operation():
    await asyncio.sleep(1)  # 模拟异步操作

asyncio.run(my_coroutine())

五、并发执行协程

并发执行协程是协程编程的一个重要特性,它允许多个协程同时执行,以提高程序的性能和响应能力。

1、并发与并行

  • 在讨论并发执行协程之前,需要明确并发和并行的概念。
  • 并发是指多个任务在同一时间段内交替进行,通过时间片轮转或调度算法来实现任务切换,给人一种同时执行的感觉。
  • 并行是指多个任务同时执行,需要具备多个执行单元(例如多个 CPU 核心)才能实现真正的并行执行。

2、并发执行协程的实现

  • 在协程编程中,通过事件循环(Event Loop)来实现并发执行协程。
  • 事件循环负责调度和执行协程对象,根据协程的状态和事件的发生情况,决定哪个协程继续执行、哪个协程挂起等。
  • 事件循环利用异步操作和挂起点的机制,使得协程能够在等待异步操作完成时暂停执行,并在异步操作完成后恢复执行。

3、并发执行的优势

  • 并发执行协程可以将多个任务交替执行,避免了阻塞等待的情况,提高了程序的性能和响应能力。
  • 通过合理的任务调度,可以在等待某个协程的异步操作时,切换到执行其他协程,从而充分利用 CPU 资源。

4、并发执行的实现方式

  • 使用 asyncio.gather() 函数:asyncio.gather() 函数可以接收多个协程对象作为参数,并将它们并发执行。它返回一个协程对象,可以通过 await 等待所有协程执行完成。

    python 复制代码
    import asyncio
    
    async def coroutine1():
        # 协程1逻辑
    
    async def coroutine2():
        # 协程2逻辑
    
    async def main():
        await asyncio.gather(coroutine1(), coroutine2())  # 并发执行协程1和协程2
    
    asyncio.run(main())
  • 使用 asyncio.wait() 函数:asyncio.wait() 函数可以接收一个协程对象的集合,并返回一个元组,包含已完成和未完成的协程集合。可以通过 await 等待所有协程执行完成。

    python 复制代码
    import asyncio
    
    async def coroutine1():
        # 协程1逻辑
    
    async def coroutine2():
        # 协程2逻辑
    
    async def main():
        coroutines = [coroutine1(), coroutine2()]
        done, pending = await asyncio.wait(coroutines)  # 并发执行协程集合
    
    asyncio.run(main())
相关推荐
databook3 小时前
Manim实现闪光轨迹特效
后端·python·动效
Juchecar5 小时前
解惑:NumPy 中 ndarray.ndim 到底是什么?
python
用户8356290780515 小时前
Python 删除 Excel 工作表中的空白行列
后端·python
Json_5 小时前
使用python-fastApi框架开发一个学校宿舍管理系统-前后端分离项目
后端·python·fastapi
数据智能老司机12 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机13 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机13 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机13 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i13 小时前
drf初步梳理
python·django
每日AI新事件13 小时前
python的异步函数
python