Python笔记四之协程

本文首发于公众号:Hunter后端

原文链接:Python笔记四之协程

协程是一种运行在单线程下的并发编程模型,它的特点是能够在一个线程内实现多个任务的并发操作,通过在执行任务时主动让出执行权,让其他任务继续执行,从而实现并发。

以下所有的代码都是在 Python 3.8 版本中运行。

本篇笔记目录如下:

  1. asyncio
    async
    await
  2. 并发运行协程任务
    1. 获取协程返回结果
    2. asyncio.gather()
  3. 报错处理
  4. 超时处理
  5. 用协程的方式访问网络接口

1、asyncio

在 Python 中,协程使用 asyncio 模块来实现,asyncio 是用来编写并发代码的库,使用的 async/await 语法。

async

我们使用 async 做前缀将普通函数变成异步函数,比如:

import asyncio 
import timeasync def say_after(delay, what):now = time.time()await asyncio.sleep(delay)  print(what, " 花时间:", time.time() - now)return time.time(async def main():print("started at: ", time.strftime("%X"))await say_after(1, "hello")await say_after(2, "world")print("finished at: ", time.strftime("%X"))asyncio.run(main())

函数前加上 async 就将其变成了一个异步函数,在这里我们通过 asyncio.run() 的方式在外层调用异步函数。

await

在 main() 函数里,我们通过 await 的方式表示在异步函数,也就是 main 函数里暂停当前的操作,等待后面跟着的 say_after() 异步函数执行完成。

await say_after() 就是我们前面说过的在执行任务的时候主动让出执行权,让其他任务执行。

2、并发运行协程任务

在上面 main() 函数的两个 await say_after() 中,可以看到两次 print() 出来的时间差约为 3s,因为我们两次调用 say_after() 分别用了 1 秒和 2 秒时间,所以这两次 await 操作是暂停当前任务的串行执行。

如果我们想要实现协程的并发操作,可以使用 asyncio.create_task()

async def main():task1 = asyncio.create_task(say_after(1, "hello"))task2 = asyncio.create_task(say_after(2, "hello"))print("started at: ", time.strftime("%X"))await task1await task2print("finished at: ", time.strftime("%X"))asyncio.run(main())# started at:  11:40:03
# hello  花时间: 1.0013182163238525
# hello  花时间: 2.001201868057251
# finished at:  11:40:05

say_after() 函数中,有一个 await asyncio.sleep() 的操作,它的作用是在协程中主动挂起当前任务一段时间,并将控制权返回给事件循环,允许其他协程继续执行。

它模拟了在协程中等待一定时间的行为,比如在协程中发起网络请求后,协程会挂起等待网络请求的响应返回,或者异步 IO 操作中的等待 IO 操作完成等。

所以在上面这个函数操作中,我们通过 asyncio.create_task() 将协程函数 say_after() 添加到事件循环中进行自动调度,并在合适的时机执行。

所以在上面的操作中,程序检测到 say_after() 中需要进行 sleep 的操作,就会自动对其进行调度,切换到事件循环的下一个任务执行,这样就实现了协程任务的并发操作。

也因此,程序执行的整体时间会比前面的操作快 1 秒左右。

获取协程返回结果

协程的返回结果直接在 await 前赋值即可:

result1 = await task1
print(result1)
asyncio.gather()

asyncio.gather() 也可以用于并发执行协程任务,但是与 asyncio.create_task() 略有不同。

create_task() 的操作是将协程函数添加到事件循环中进行调度,返回的是一个 Task 对象,而 gather() 则可以直接接收多个协程任务并发执行,并等待他们全部完成,返回 Future 对象表示任务结果。

gather() 的使用方法如下:

async def main():results = await asyncio.gather(say_after(1, "hello"),say_after(2, "world"),)

asyncio.gather() 除了可以接收异步函数,还可以接受 asyncio.create_task() 返回的结果,也就是返回的 task 对象,比如下面的操作也是合法的:

async def main():task = asyncio.create_task(say_after(1, "hello"))results = await asyncio.gather(say_after(1, "hello"),say_after(2, "world"),task,)

3、报错处理

如果在并发操作中有一些报错,比如下面的示例:

import asyncio
import timeasync def say_after(delay, what):now = time.time()await asyncio.sleep(delay)print(what, " 花时间:", time.time() - now)return time.time()async def say_error(delay, err_msg="error"):await asyncio.sleep(delay)raise Exception(err_msgasync def main():results = await asyncio.gather(say_after(1, "hello"),say_error(2, "error"),say_after(3, "world"),)print(results)asyncio.run(main())

在上面的操作中,三个协程函数,在执行到第二个的时候,程序其实就直接返回报错了,如果想要忽略报错继续执行之后的操作,可以加上 return_exceptions 参数,设为 True

async def main():results = await asyncio.gather(say_after(1, "hello"),say_error(2, "error"),say_after(3, "world"),return_exceptions=True,)print(result)# [1691045418.774685, Exception('error'), 1691045420.774549]

这样就会将报错信息直接也返回,且执行之后的协程函数。

4、超时处理

我们可以为协程函数执行的时间预设一个时间,如果超出这个时间则返回报错信息,我们可以使用 asyncio.wait_for(),比如:

async def main_4():results = await asyncio.gather(say_after(1, "hello"),say_error(2, "error"),asyncio.wait_for(say_after(30, "world"), timeout=3),return_exceptions=True,)print(results)
# [1691045925.265661, Exception('error'), TimeoutError()]

在上面的操作中,我们给第三个任务加了个 3 秒的超时处理,但是该协程会执行 30 秒,所以返回的报错里是一个 TimeoutError()

5、用协程的方式访问网络接口

接下来我们用协程的方式来访问一个接口,与不用协程的方式进行比对。

首先我们建立一个服务端,用 Django、Flask都可以,只是提供一个访问接口,以下是用 Flask 建立的示例:

from flask import Flask
import timedef create_app():app = Flask(__name__)@app.route("/test")def test():time.sleep(1)return str(time.time())return app

运行这段代码就提供了我们需要的服务器接口。

使用协程的方式访问接口我们这里用到的是 aiohttp,是第三方库,需要提前安装:

pip3 install aiohttp==3.8.5

进行测试的脚本如下:

import asyncio
import aiohttp
import requests
import timeCALL_TIMES = 10000def connect_url(url):return requests.get(url)def run_connect_url(url):results = []for i in range(CALL_TIMES):result = connect_url(url)results.append(result)return resultsasync def connect_url_by_session(session, url):async with session.get(url) as response:return await response.text()async def run_connect(url):async with aiohttp.ClientSession() as session:tasks = []for i in range(CALL_TIMES):tasks.append(connect_url_by_session(session, url))results = await asyncio.gather(*tasks)return resultsif __name__ == "__main__":url = "http://127.0.0.1:5000/test"t1 = time.time()run_connect_url(url)print(f"串行调用次数: {CALL_TIMES},耗时:{time.time() - t1}")t2 = time.time()asyncio.run(run_connect(url))print(f"协程调用次数:{CALL_TIMES},耗时:{time.time() - t2}")

在这里,aiohttp 的具体用法看代码即可,我们可以通过修改 CALL_TIMES 来修改调用次数,我这里调用 1000 次和 10000 次的结果分别如下:

串行调用次数: 1000,耗时:3.2450389862060547
协程调用次数:1000,耗时:1.3642120361328125串行调用次数: 10000,耗时:32.830286741256714
协程调用次数:10000,耗时:12.519049882888794

可以看到使用协程的方式对于接口的访问效率有了明显的提升。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/753842.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端面试拼图-实践经验

摘要:最近,看了下慕课2周刷完n道面试题,记录并添加部分可参考的文档,如下... 1. H5页面如何进行首屏优化? 路由懒加载 适用于SPA(不适用MPA) 本质就是路由拆分,有限保证首页加载 服…

ThreadLocal-内存泄露问题

ThreadLocal概述 ThreadLocal是多线程中对于解决线程安全的一个操作类,它会为每个线程都分配一个独立的线程副本从而解决了变量并发访问冲突的问题。ThreadLocal 同时实现了线程内的资源共享案例:使用JDBC操作数据库时,会将每一个线程的Conn…

从0到1理解MySQL的事务和ACID特性

从0到1理解MySQL的事务和ACID特性 在开始之前,我们先来了解一下MySQL的事务是什么,以及ACID是什么。 事务是什么? 事务可以简单地理解为一组数据库操作的集合,它们一起被视为一个不可分割的工作单元。事务要么全部成功执行&…

SpringBoot ApplicationListener实现发布订阅模式

文章目录 前言一、Spring对JDK的扩展二、快速实现发布订阅模式 前言 发布订阅模式(Publish-Subscribe Pattern)通常又称观察者模式,它被广泛应用于事件驱动架构中。即一个事件的发布,该行为会通过同步或者异步的方式告知给订阅该事件的订阅者。JDK中提供…

springboot 查看和修改内置 tomcat 版本

解析Spring Boot父级依赖 去到项目的根pom文件中&#xff0c;找到parent依赖&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>${springboot.version}…

Error response from daemon Get server gave HTTP response to HTTPS client

使用docker compose拉起docker镜像时&#xff0c;若出现如下报错 Error response from daemon: Get "https://devops.test.cn:5000/v2/": http: server gave HTTP response to HTTPS client表示Docker守护进程无法从指定url获取响应&#xff0c; 可能原因有以下&…

苍穹外卖-day09:用户端历史订单模块(理解业务逻辑),商家端订单管理模块(理解业务逻辑),校验收货地址是否超出配送范围(相关API)

用户端历史订单模块 1. 查询历史订单&#xff08;分页查询&#xff09; 1.1 需求分析和设计 产品原型&#xff1a; 业务规则 分页查询历史订单可以根据订单状态查询展示订单数据时&#xff0c;需要展示的数据包括&#xff1a;下单时间、订单状态、订单金额、订单明细&#…

C解析YAML

在C语言中解析YAML文件&#xff0c;你可以使用如libyaml等专门的YAML解析库。以下是使用libyaml库进行YAML解析的一个基本示例。libyaml是一个用来解析和生成YAML数据的C库。 首先&#xff0c;你需要确保已经正确安装了libyaml库。对于许多Linux发行版&#xff0c;你可以使用包…

软考76-上午题-【面向对象技术3-设计模式】-创建型设计模式01

一、创建型设计模式一览 二、创建型设计模式 2-1、创建型设计模式的概念 一个类创建型模式使用继承改变被实例化的类&#xff1b; 一个对象创建型模式将实例化委托给另一个对象。 对应java的new一个对象。 2-2、简单工厂模式&#xff08;静态工厂方法&#xff09; 简单工厂…

一文解读ISO26262安全标准:概念阶段

一文解读ISO26262安全标准&#xff1a;概念阶段 1 相关项定义2 安全生命周期启动3 危害分析和风险评估 HaRa4 功能安全概念 由上一篇文章知道&#xff0c;安全生命周期包含概念阶段、产品开发阶段、生产发布后续阶段。本文详细解读概念阶段要进行的安全活动。 本部分规定了车辆…

猫头虎分享已解决Bug || TypeError: Cannot interpret ‘float‘ value as integer.

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

利用自定义 URI Scheme 在 Android 应用中实现安全加密解密功能

在现代移动应用开发中&#xff0c;安全性和用户体验是至关重要的考虑因素。在 Android 平台上&#xff0c;开发人员可以利用自定义 URI Scheme 和 JavaScript 加密解密技术来实现更安全的数据传输和处理。本文将介绍如何在 Android 应用中注册自定义 URI Scheme&#xff0c;并结…

蓝桥杯C++大学B组一个月冲刺记录2024/3/18

蓝桥杯C大学B组一个月冲刺记录2024/3/18 规则:每日三题 昨天因为前妻姐emo上了,静下来思考了点东西,就没做题啦.今日补上! 另外:博客浏览量破万了,写的东西有人看还是很开心的 1.母亲的牛奶 农夫约翰有三个容量分别为 A,B,C升的挤奶桶。 最开始桶 A和桶 B都是空的&#xff0c;…

计算机组成原理——自己制作一个cpu

cpu包括单周期cpu、中断cpu、多周期cpu 代码实现之后在实验箱看效果&#xff0c;并且看波形图 单周期波形 中断cpu 多周期cpu 1.单周期CPU总体电路图 如图是一个简单的基本上能够在单周期CPU上完成所要求设计的指令功能的数据通路和必要的控制线路图。其中指令和数据各存储在不…

【2024-03-18】顺丰春招笔试两道编程题解

恭喜发现宝藏&#xff01;搜索公众号【TechGuide】回复公司名&#xff0c;解锁更多新鲜好文和互联网大厂的笔经面经。 作者TechGuide【全网同名】 订阅专栏&#xff1a; 【专享版】2024最新大厂笔试真题解析&#xff0c;错过必后悔的宝藏资源&#xff01; 第一题&#xff1a;转…

超越想象的数据可视化:五大工具引领新潮流

在数据分析领域&#xff0c;数据可视化工具是每位分析师的得力助手。它们能够将复杂的数据转化为直观、易懂的图表和图像&#xff0c;帮助分析师快速洞察数据背后的规律与趋势。下面&#xff0c;我将从数据分析师的角度&#xff0c;为大家介绍五个常用的数据可视化工具。 一、…

【vue.js】文档解读【day 5】| ref模板引用

如果阅读有疑问的话&#xff0c;欢迎评论或私信&#xff01;&#xff01; 本人会很热心的阐述自己的想法&#xff01;谢谢&#xff01;&#xff01;&#xff01; 文章目录 模板引用前言访问模板引用模板引用与v-if、v-show的结合v-for中的模板引用函数模板引用 模板引用 前言 …

Vue.js+SpringBoot开发食品生产管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 加工厂管理模块2.2 客户管理模块2.3 食品管理模块2.4 生产销售订单管理模块2.5 系统管理模块2.6 其他管理模块 三、系统展示四、核心代码4.1 查询食品4.2 查询加工厂4.3 新增生产订单4.4 新增销售订单4.5 查询客户 五、…

二蛋赠书十八期:《一本书讲透Elasticsearch:原理、进阶与工程实践》

Elasticsearch 是一种强大的搜索和分析引擎&#xff0c;被广泛用于各种应用中&#xff0c;以其强大的全文搜索能力而著称。 不过&#xff0c;在日常管理 Elasticsearch 时&#xff0c;我们经常需要对索引进行保护&#xff0c;以防止数据被意外修改或删除&#xff0c;特别是在进…

PC-DARTS: PARTIAL CHANNEL CONNECTIONS FOR MEMORY-EFFICIENT ARCHITECTURE SEARCH

PC-DARTS&#xff1a;用于内存高效架构搜索的部分通道连接 论文链接&#xff1a;https://arxiv.org/abs/1907.05737 项目链接&#xff1a;https://github.com/yuhuixu1993/PC-DARTS ABSTRACT 可微分体系结构搜索(DARTS)在寻找有效的网络体系结构方面提供了一种快速的解决方案…