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 密集型任务中可能不如多线程效果好。在实际应用中,需要根据具体的需求和场景选择适合的并发模型。

二、async 和 await 关键字

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

1、async 关键字

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

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

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

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

2、await 关键字

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

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

三、协程对象

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

1、创建协程对象

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

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

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

2、等待协程对象的执行

  • 协程对象可以被等待和执行,以获取其执行结果。这通常通过在其他协程或异步函数内使用 await 关键字来实现。
  • 在等待协程对象时,当前的协程会暂时挂起,让出事件循环的控制权,等待协程对象的执行完成,并获得其返回值。
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)。
  • 初始时,协程对象处于等待状态,表示它可以被等待执行。当协程对象被事件循环调度执行时,进入运行状态。最终,当协程对象执行完成时,进入完成状态。
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() 方法来创建新的事件循环对象。
import asyncio

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

2、注册协程对象

  • 使用事件循环的 create_task() 方法可以将协程对象注册到事件循环中,以便事件循环调度其执行。
  • 注册后的协程对象会在事件循环的调度下按照顺序执行。
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() 方法,并传入需要运行的协程对象,可以启动事件循环并运行协程直到其完成。
  • 在运行过程中,事件循环会根据协程的状态和事件的发生情况,决定哪个协程继续执行、哪个协程挂起等。
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() 方法来启动事件循环,并使其一直运行,直到手动停止。
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 表达式来实现,它用于等待一个异步操作的完成,并将控制权交给事件循环。
  • 在挂起点之前的代码会被执行,但在挂起点之后的代码在等待期间不会执行,直到挂起点恢复执行。
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、挂起和恢复

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

  • 27
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寒秋丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值