首页 > 编程语言 >Python 异步编程原理篇之新旧协程实现对比

Python 异步编程原理篇之新旧协程实现对比

时间:2024-01-16 09:11:43浏览次数:33  
标签:协程 Python await 生成器 yield 关键字 原理篇 asyncio

image

协程的发展流程

再来回顾一下协程的发展流程:
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等关键字为基础的协程实现
两种协程的实现方式在协程发展史上有一段交集,并且旧协程基于生成器的协程语法让生成器和协程两个概念混淆,所以对学习者会造成一定的困扰。本篇主要说明两种协程的实现方式的差异。

旧协程回顾

旧协程以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生成器的能力实现

新协程回顾

新协程是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 关键字实现

新酒协程实现的对比

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

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

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

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

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

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

await 和 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。这些内容是协程原理的核心知识,理解有难度。

标签:协程,Python,await,生成器,yield,关键字,原理篇,asyncio
From: https://www.cnblogs.com/goldsunshine/p/17966295

相关文章

  • python爬虫利器之Playwright
    Playwright是微软在2020年初开源的新一代自动化测试工具,它的功能类似于Selenium、Pyppeteer等,都可以驱动浏览器进行各种自动化操作。它的功能也非常强大,对市面上的主流浏览器都提供了支持,API功能简洁又强大。虽然诞生比较晚,但是现在发展得非常火热。1.Playwright的特点......
  • Python第四天学习笔记
    Python的用户交互input('请插入vip卡')input('输入密码')请插入vip卡23333输入密码6666'6666'print('*'*100)****************************************************************************************************print('*'......
  • python网站创建016:BootStrap
    什么是BootStrap?:是一个别人写好的javascript和css的代码,如果在开发项目的时候省点事,就可以在它的基础上来修改使用使用教程:https://www.runoob.com/bootstrap/bootstrap-tutorial.html下载:https://getbootstrap.net/   下载之后解压,解压之后可以随意打开一个文件,比如cs......
  • python中json.dumps() 与json.dump(),json.load()与json.loads()区别?
    json.dumps()将Python对象转换为JSON字符串,并返回该字符串。而json.dump()将Python对象转换为JSON字符串,并将该字符串写入文件。json.dumps()接受一个Python对象作为参数,而json.dump()接受两个参数:一个Python对象和一个写入数据的文件对象。json.dump()生......
  • Python 中read()、 readline() 、readlines()三者之间的区别?
    read()方法用于一次性读取整个文件的内容,并将其作为一个字符串返回。readline()方法用于逐行读取文件的内容。每次调用readline()方法,它会读取文件的下一行,并将其作为一个字符串返回。readlines()方法用于将文件的所有行读取到一个列表中。每一行都是列表中的一个元素,列表按照文......
  • 深入了解 Python MongoDB 操作:排序、删除、更新、结果限制全面解析
    PythonMongoDB排序对结果进行排序使用sort()方法对结果进行升序或降序排序。sort()方法接受一个参数用于“字段名”,一个参数用于“方向”(升序是默认方向)。示例按名称按字母顺序对结果进行排序:importpymongomyclient=pymongo.MongoClient("mongodb://localhost:270......
  • Python第三天学习笔记
    执行Python的两种形式第一种:交互式,在cmd中运行优点:直接给出结果缺点:无法保存jupyter对这一种进行了封装第二种:命令行式,通过cmd中输入Python3文本.txt文件可以,.py文件也可以,命令行式可以识别任何文本文件优点:可以保存缺点:无法及时得到报错反馈1.2.1三个步骤编写Py......
  • 数学建模入门笔记(1)——Python pulp库解线性规划问题
    参考:Python求解线性规划——PuLP使用教程-Only(AR)-博客园(cnblogs.com)1.Definethemodelmodel=pl.LpProblem(name="",sense=pl.LpMaximize)name模型的名字sense模型的类型(pl.LpMaximize/pl.LpMinimize)2.Definethedecisionvariables用x[i]存储变量,命名为xi......
  • python selenium元素定位
    1.ID元素定位基于元素属性中的id的值来进行定位,id是一个标签的唯一属性值可以通过id属性来唯一定位一个元素,是首选的元素定位方式,动态ID不做考虑。driver.find_element_by_id('id')driver.find_element(By.ID,'id')2.name元素定位基于元素属性中的name的值来进行定位,但name并不......
  • # yyds干货盘点 # 盘点一个Python发票识别报错问题的处理案例
    大家好,我是皮皮。一、前言前几天在Python免费交流群【PJW】问了一个Python发票识别报错的问题,下图是他的报错截图,但是他自己看不出来哪里有问题,百度方面其实一问应该也有答案的,可是他就是有些找不到,然后找群里的好心人求助。后来【果冻和布丁】有GPT,找他帮忙问了一圈。二、实现过程......