4

Python 异步编程原理篇之新旧协程实现对比 - 金色旭光

 8 months ago
source link: https://www.cnblogs.com/goldsunshine/p/17966295
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

image

1|0协程的发展流程

再来回顾一下协程的发展流程:
python2.5 为生成器引用.send()、.throw()、.close()方法
python3.3 为引入yield from,可以接收返回值,可以使用yield from定义协程
Python3.4 加入了asyncio模块
Python3.5 增加async、await关键字,在语法层面的提供支持
python3.7 使用 async def + await 的方式定义协程
python3.10 移除 以 yield from 的方式定义协程

旧协程是指以yieldyield from等生成器语法为基础的协程实现
新协程是指以asyncioasyncawait等关键字为基础的协程实现
两种协程的实现方式在协程发展史上有一段交集,并且旧协程基于生成器的协程语法让生成器和协程两个概念混淆,所以对学习者会造成一定的困扰。本篇主要说明两种协程的实现方式的差异。

2|0旧协程回顾

旧协程以yield关键字为核心,通过yield关键提供的代码执行暂停、恢复的能力,实现函数交替的执行,cpu的转让等能力。



import time

def consume(): r = '' while True: n = yield r print(f'[consumer] 开始消费 {n}...') time.sleep(1) r = f'{n} 消费完成'

def produce(c): next(c) n = 0 while n < 5: n = n + 1 print(f'[producer] 生产了 {n}...') r = c.send(n) print(f'[producer] consumer return: {r}') c.close()

if __name__=='__main__': c = consume() produce(c)

执行结果:



[producer] 生产了 1... [consumer] 开始消费 1... [producer] consumer return: 1 消费完成 [producer] 生产了 2... [consumer] 开始消费 2... [producer] consumer return: 2 消费完成 [producer] 生产了 3... [consumer] 开始消费 3... [producer] consumer return: 3 消费完成 [producer] 生产了 4... [consumer] 开始消费 4... [producer] consumer return: 4 消费完成 [producer] 生产了 5... [consumer] 开始消费 5... [producer] consumer return: 5 消费完成

结果分析:
当消费者consume执行到n = yield r时,流程暂停,将cpu交还给调用方produce

asyncio初识篇中提到过,协程最重要的两个因素是事件循环+ 任务。用yield实现的协程中,consumeproduce中的 while循环共同作用下实现了一个事件循环的功能,yieldsend实现了任务的暂停和继续执行。

总结来说协程需要的两个能力事件循环任务暂停和继续,在旧协程中的实现分别是:

  1. 事件循环通过手动编写while循环代码实现
  2. 代码暂停继续执行通过yield生成器的能力实现

3|0新协程回顾

新协程是asyncioasyncawait等关键字实现的。新协程是基于事件循环机制实现的,核心能力包括事件循环,任务,回调机制等。三者提供的能力分别是

  1. asyncio 提供了事件循环
  2. async 提供了协程标识
  3. await 提供了流程挂起能力


import asyncio

async def coro1(): print("start coro1") await asyncio.sleep(2) print("end coro1")

async def coro2(): print("start coro2") await asyncio.sleep(1) print("end coro2")

# 创建事件循环 loop = asyncio.get_event_loop()

# 创建任务 task1 = loop.create_task(coro1()) task2 = loop.create_task(coro2())

# 运行协程 loop.run_until_complete(asyncio.gather(task1, task2))

# 关闭事件循环 loop.close()



start coro1 start coro2 end coro2 end coro1

结果分析:
coro1执行到 await asyncio.sleep(2)时,流程挂起,将cpu交还给事件循环,等待事件循环的下一次调度,而事件循环调度到coro2继续执行。

协程的两个重要能力事件循环任务暂停和继续 ,分别的实现者:

  1. 事件循环通过asyncio提供的loop实现
  2. 程序挂起通过 await 关键字实现

4|0新旧协程实现的对比

asyncioyield 是用于实现异步编程的两种不同的机制。

yield 是一种用于生成器(Generator)函数的关键字,用于创建可暂停和恢复执行的函数。当一个函数中包含 yield 语句时,它会返回一个生成器对象,可以通过调用生成器的 next() 方法或使用 for 循环来逐步迭代生成器函数中的值。

通过使用 yield,我们可以将一个函数分割成多个代码块,并在每个代码块之间进行切换执行。这使得我们可以在函数执行过程中临时挂起函数的执行,然后再次恢复执行。

asyncio 是 Python 提供的标准库,用于编写异步代码。它基于事件循环(Event Loop)模式,允许我们在单线程中处理多个并发任务,并通过协程(Coroutine)来管理异步操作。

asyncio 使用了 asyncawait 这两个关键字来定义协程函数。在协程函数中可以使用 await 关键字来暂停当前协程的执行,等待某个异步操作的完成,然后恢复执行。

总结来说:
旧协程:通过yield关键字的暂停和恢复执行的能力实现协程
新协程:通过事件循环机制,await关键字挂起流程能力实现协程

5|0await 和 yield 的关系

await 关键字和 yield 关键字都可以用于控制流的暂停和恢复,都属于python的关键字,但是它们在协程的实现上有所不同。

相同点:

  1. 控制流暂停和恢复:无论是 await 还是 yield,它们都可以使代码在某个点暂停执行,并在稍后的时间点继续执行。
  2. 协程支持awaityield 都与协程(Coroutine)密切相关。它们都能够用于定义和管理协程,使得异步代码的编写更加简单和易读。

区别:

  1. 语法差异await 是 Python 3.5 引入的关键字,用于异步函数中暂停执行等待异步操作完成。而 yield 是早期协程的关键字,主要用于生成器(Generator)函数,用于创建迭代器和实现惰性计算,早期通过生成器的能力来实现协程。
  2. 语义
  • await 表示当前协程需要等待一个异步操作的完成,并挂起执行,让其他任务有机会执行。

  • yield 是将执行的控制权交给调用方,同时保存函数的状态,以便在下次迭代时从上一次暂停的位置恢复执行。

    await将程序挂起,让事件循环调度新的任务。yield将程序挂起,等待调用方的下一步指令。
  1. 上下文await 必须在异步上下文中使用,例如在异步函数中或者在 async with 块中。而 yield 可以在普通函数中使用,即使没有使用协程的上下文。
  2. 返回值yield 返回生成器对象,通过调用 next() 方法或使用 for 循环逐步迭代生成器中的值。而 await 返回一个可等待对象(Awaitable),它可以是 FutureTaskCoroutine 等。

总结:
await 不是通过 yield 来实现的程序暂停和执行,两者有相似的能力,但完全没有调用关系,都是属于python关键字。

  • await 适用于异步编程场景,用于等待异步操作的完成,同时支持更灵活的协程管理。
  • yield 则主要用于生成器函数,用于实现迭代器和惰性计算。

它们在应用场景和语法上存在一些差异,但都为我们提供了控制流的暂停和恢复的能力。

以上就是新旧协程的实现方法,对比了两种协程的实现方法,比较了yield关键字既作为生成器又实现协程有点混淆的用法,比较了都可以暂停恢复的关键字yield和await。这些内容是协程原理的核心知识,理解有难度。

__EOF__


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK