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())
相关推荐
轻口味32 分钟前
命名空间与模块化概述
开发语言·前端·javascript
晓纪同学1 小时前
QT-简单视觉框架代码
开发语言·qt
威桑1 小时前
Qt SizePolicy详解:minimum 与 minimumExpanding 的区别
开发语言·qt·扩张策略
飞飞-躺着更舒服2 小时前
【QT】实现电子飞行显示器(简易版)
开发语言·qt
明月看潮生2 小时前
青少年编程与数学 02-004 Go语言Web编程 16课题、并发编程
开发语言·青少年编程·并发编程·编程与数学·goweb
明月看潮生2 小时前
青少年编程与数学 02-004 Go语言Web编程 17课题、静态文件
开发语言·青少年编程·编程与数学·goweb
Java Fans2 小时前
C# 中串口读取问题及解决方案
开发语言·c#
盛派网络小助手2 小时前
微信 SDK 更新 Sample,NCF 文档和模板更新,更多更新日志,欢迎解锁
开发语言·人工智能·后端·架构·c#
算法小白(真小白)2 小时前
低代码软件搭建自学第二天——构建拖拽功能
python·低代码·pyqt
唐小旭2 小时前
服务器建立-错误:pyenv环境建立后python版本不对
运维·服务器·python