Python运维之协程

目录

一、定义协程

二、并发

三、异步请求


协程是一种轻量级的线程,它通过保存和恢复寄存器上下文和栈来实现调度切换,从而保留函数执行的状态

这种机制使得协程在处理I/O密集型任务时效率较高,因为它们可以在I/O操作期间让出CPU,以执行其他任务。与多线程相比,协程在同一线程内进行调用,减少了上下文切换的开销。

简而言之,协程通过在函数执行过程中灵活地让出和收回控制权提高了程序的并发性能

一、定义协程

python3.4加入协程的概念,以生成器对象为基础。Python3.5增加了async/await,下面以asyncio为基础介绍协程的使用。

 import asyncioimport time​async def task():print(f"{time.strftime('%H:%M:%S')} task 开始")time.sleep(2)print(f"{time.strftime('%H:%M:%S')} task 结束")​coroutine = task()print(f"{time.strftime('%H:%M:%S')} 产生协程对象 {coroutine},函数并未被调用")loop = asyncio.get_event_loop()print(f"{time.strftime('%H:%M:%S')} 开始调用协程任务")start = time.time()loop.run_until_complete(coroutine)end = time.time()print(f"{time.strftime('%H:%M:%S')} 结束调用协程任务,耗时{end - start} 秒")

提示:首先引入asyncio,主要才可以使用async和await关键字(async定义一个协程await用于临时挂起一个函数或方法的执行),接着使用async定义一个协程方法,然后直接调用该方法,但该方法没有被执行,而是返回一个coroutine协程对象。 使用get_event_loop()方法创建一个事件循环loop,并调用loop对象的run_until_complete()方法协程注册到事件循环loop中,然后启动,这才完成执行。

我们还可以为任务绑定回调函数

 import asyncioimport time​async def task():print(f"{time.strftime('%H:%M:%S')} task 开始")time.sleep(2)print(f"{time.strftime('%H:%M:%S')} task 结束")return "运行结束"​def callback(task):print(f"{time.strftime('%H:%M:%S')} 回调函数开始执行")print(f"状态:{task.result()}")​coroutine = task()print(f"{time.strftime('%H:%M:%S')} 产生协程对象 {coroutine},函数并未被调用")task = asyncio.ensure_future(coroutine)task.add_done_callback(callback)loop = asyncio.get_event_loop()print(f"{time.strftime('%H:%M:%S')} 开始调用协程任务")start = time.time()loop.run_until_complete(task)end = time.time()print(f"{time.strftime('%H:%M:%S')} 结束调用协程任务,耗时{end - start} 秒")

定义了一个协程方法和一个普通方法作为回调函数,回调函数接收一个参数是task对象,asyncio.ensure_future(coroutine)可以返回task对象add_done_callback()为task对象增加一个回调任务。这样我们就定义好了一个coroutine对象和一个回调方法,执行的结果是当couroutine对象执行完毕之后,就去执行声明的callback方法。

二、并发

上述之定义了一个协程任务,如果要多次并尽可能提高效率,可以定义一个task列表,然后使用asyncio的wait()方法执行即可:

 import asyncioimport time​async def task():print(f"{time.strftime('%H:%M:%S')} task 开始")# 异步调用asynico.sleep(1):await asyncio.sleep(2)# time.sleep(2)time.sleep(2)print(f"{time.strftime('%H:%M:%S')} task 结束")return "运行结束"​# 获取EventLoop:loop = asyncio.get_event_loop()# 执行coroutinetasks = [task() for _ in range(5)]start = time.time()loop.run_until_complete(asyncio.wait(tasks))loop.close()end = time.time()print(f"用时{end - start}")

关键字await后面的对象必须是以下类型之一:

  • 一个原生coroutine对象
  • 一个由types.coroutine()修饰的生成器,这个生成器可以返回coroutine对象
  • 一个包含await方法的对象返回的一个迭代器

asyncio.sleep(2)是一个由coroutine修饰的生成器对象,表示等待2秒。

三、异步请求

以常用的网络请求为例,网络请求较多的就是I/O密集型任务。

启动一个简单的Web服务器

 from flask import Flaskimport time​app =  Flask(__name__)​@app.route('/')def index():time.sleep(3)return 'Hello world!'​if __name__ == '__main__':app.run(threaded=True)      # 表明多线程模式启动

如果不开启多线程模式,那么同一时刻遇到多个请求时,只能顺次处理,这样即使我们使用协程异步请求这个服务,也只能一个一个排队。

 import asyncioimport requestsimport time​start = time.time()​async def request():url = 'http://127.0.0.1:5000'print(f'{time.strftime("%H:%M:%S")} 请求 {url}')response = requests.get(url)print(f'{time.strftime("%H:%M:%S")} 得到响应 {response.text}')​tasks = [asyncio.ensure_future(request()) for _ in range(5)]loop = asyncio.get_event_loop()loop.run_until_complete(asyncio.wait(tasks))​end = time.time()print(f"耗时{end-start}")

耗时15秒,其实要实现异步处理,必须先有挂起的操作,当一个任务需要等待I/O结果时,可以挂起当前任务,让出CPU的控制权,转去执行其他任务,这样才能充分利用好资源。上述代码串行走,没有实现挂起

要实现异步,使用await将耗时等待的操作挂起让出控制权。当协程执行时遇到await时间循环就会将本协程挂起,转去执行别的协程,直到其他的协程挂起或执行完毕,修改代码:

 import asyncioimport requestsimport time​async def get(url):return requests.get(url)​async def request():url = 'http://127.0.0.1:5000'print(f'{time.strftime("%H:%M:%S")} 请求 {url}')response = await get(url)print(f'{time.strftime("%H:%M:%S")} 得到响应 {response.text}')​start = time.time()tasks = [asyncio.ensure_future(request()) for _ in range(5)]loop = asyncio.get_event_loop()loop.run_until_complete(asyncio.wait(tasks))end = time.time()print(f"耗时{end-start}")

上述代码将请求页面的方法封装为一个coroutine读写,在request方法中尝试使用await挂起当前执行的I/O,发现还是15s,原来request不是异步请求,aiohttp是一个支持异步请求的库,将其配合使用即可实现异步请求操作:

 import asyncioimport aiohttpimport time​now = lambda :time.strftime("%H:%M:%S")​async def get(url):async with aiohttp.ClientSession() as session:  # 使用异步上下文管理器response = await session.get(url)result = await response.text()return resultasync def request():url = 'http://127.0.0.1:5000'print(f'{now()} 请求 {url}')result = await get(url)print(f'{now()} 得到响应 {result}')​start = time.time()tasks = [asyncio.ensure_future(request()) for _ in range(5)]loop = asyncio.get_event_loop()loop.run_until_complete(asyncio.wait(tasks))end = time.time()print(f"耗时{end-start}")

运行时间只有3秒,扩大20倍还是3秒。可见,异步协程在爬虫项目值速度提升是非常可观了。

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

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

相关文章

nacos在没有指定数据源的情况下默认使用什么数据库?

在没有特别指定数据源的情况下,Nacos 默认使用内嵌的数据库 Derby 来存储其数据。Derby 是一个轻量级的、基于 Java 的数据库管理系统,适合于开发和测试环境,因为它简单易部署且无需额外的数据库服务器。然而,对于生产环境&#x…

祝贺嫦娥六号发射成功,思迈特再为航天项目提供数据支持和保障

近日,嫦娥六号由长征五号遥八运载火箭在中国文昌航天发射场发射成功。 据悉,嫦娥六号是中国探月工程的第六个探测器,其主要任务是前往月球背面的南极-艾特肯盆地进行科学探测和样品采集。 嫦娥六号任务不仅是技术上的挑战,也是科学…

MySQL表结构的一些设计经验分享

我们在对一张表进行设计时,还要遵守一些基本的原则,比如经常听见的“范式准则”。但范式准则过于理论,在真实业务中,不必严格遵守三范式的要求。而且有时为了性能考虑,还可以进行反范式的设计,比如在数据仓…

知乎知+广告推广该如何做?怎么收费?

知乎作为一个汇聚高质量用户群体的知识分享平台,成为了众多品牌和产品推广的优选之地。特别是知乎的“知”广告推广服务,以其精准定向、内容原生的特点,深受广告主青睐。 一、知乎知广告推广基础 1. 什么是知乎知? 知是知乎官方…

C++--String类

系列文章目录 文章目录 目录 系列文章目录 文章目录 前言 一、为什么要学习string 1.c语言的字符串 2.OJ上的使用 二、string类的接口介绍 1.string简介 2.string构造成员函数 3.operator函数 4.string容器size和length 5.重载operator[]和引用返回的意义 5.1 oper…

使用git系统来更新FreeBSD ports源码

FreeBSD跟其它系统相比一大特色就是ports系统。 The Ports Collection is a set of Makefiles, patches, and description files. Each set of these files is used to compile and install an individual application on FreeBSD, and is called a port. By default, the Po…

使用Docker+Jar方式部署微服务工程(前后端分离)看着一篇就够了

本篇教程的使用到的技术有springboot、springcloud、Nacos、Docker、Nginx部署前后端分离访问的微服务。 部署一下Nacos 首先我们需要在服务器中(或者本地部署启动一下Nacos),这里我采用服务器的方式进行部署,这里有一点不一样的…

# 从浅入深 学习 SpringCloud 微服务架构(十七)--Spring Cloud config(2)

从浅入深 学习 SpringCloud 微服务架构(十七)–Spring Cloud config(2) 一、springcloudConfig 入门案例:搭建 config 服务端 1、登录 码云:https://gitee.com/ 1)点击右上角 【】 再点击【新…

算法学习笔记(一)-快速幂

#问题的引入-对于幂次方的求解我们怎么可以最大限度的降低时间复杂度呢 #对于一个基本的幂次运算&#xff0c;c代码如下示例 long long int myPower(int base,int power) {long long int result 1 ;for (int i 1 ; i < power ; i){result * base ;}return result ; } #…

网络实验新境界,PNETLab模拟器部署指南

在网络工程领域&#xff0c;拥有一个可靠的网络实验平台至关重要。PNETLab模拟器是一款功能强大的网络仿真工具&#xff0c;它支持包括华为、华三、锐捷、思科在内的多种设备&#xff0c;并且以开源免费的形式提供&#xff0c;这使得它在业界备受青睐。 软件介绍 PNETLab&am…

K8s:二进制安装k8s(单台master)

一、安装k8s 1、拓扑图 2、系统初始化配置 #所有节点执行 systemctl stop firewalld systemctl disable firewalld iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X #永久关闭firewalld并清空iptables所有表规则 setenf…

标准输入输出流(中北大学-程序设计基础(2))

目录 题目 源码 结果示例 题目 输入三角形的三边a,b,c&#xff0c;计算三角形的面积。形成三角形的条件是ab>c,bc>a,ac>b&#xff0c;编写程序&#xff0c;输入a,b,c&#xff0c;检查a,b,c是否满足以上条件&#xff0c;如不满足&#xff0c;由cerr输出有关出错信息…