热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入解析Python协程:asyncio模块的应用与优化

在处理大规模并发请求时,传统的多线程或多进程模型往往无法有效解决性能瓶颈问题。尽管它们在处理小规模任务时能提升效率,但在高并发场景下,系统资源的过度消耗和上下文切换的开销会显著降低整体性能。相比之下,Python的`asyncio`模块通过协程提供了一种轻量级且高效的并发解决方案。本文将深入解析`asyncio`模块的原理及其在实际应用中的优化技巧,帮助开发者更好地利用协程技术提升程序性能。

为什么使用协程?

当多线程或者多进程足够多时,实际上并不能解决性能的瓶颈问题,也就是多线程和多进程对小规模的请求可以提高效率,过多的请求实际上会降低服务资源响应效率,因此协程是更好的解决文案。


什么是协程?

当一个程序遇到阻塞时,如果将这个程序挂起,然后将它的cpu权限拿出来去执行我们的其他程序,执行完后再回过头来执行这些挂起的程序,此时所有非阻塞操作已经执行完毕,最后在一起执行阻塞程序,是不是相当于做了异步。

因此,协程的作用就是检测阻塞的程序,在单进程和单线程的情况下实现异步,相比多线程和多进程效率更高。


asyncio协程模块(python3.5以上)


协程的代码基本构成


  • 特殊函数

    • 在普通函数前添加一个async关键字,则该函数就变成一个特殊的函数
    • 特殊函数的特殊之处是什么?
      • 1.特殊函数被调用后,函数内部的程序语句(函数体)没有被立即执行
      • 2.特殊函数被调用后,会返回一个协程对象
  • 协程:

    • 调用特殊函数即创建一个协程对象。
    • 因此,协程对象 = 特殊的函数 = 函数体(一组指定形式的操作)
  • 任务:

    • 任务对象就是一个高级的协程对象,即任务对象可以绑定一个回调函数
    • 任务对象 = 协程对象 == 函数体(一组指定形式的操作)
  • 事件循环:

    • 事件循环对象,,可以将其当做是一个容器,该容器是用来装载任务对象的。创建好了一个或多个任务对象后,将任务对象装载到事件循环中,启动事件循环对象,则其内部装载的任务对象对应的相关操作就会被立即执行。

import asyncio
import time
# 创建特殊函数,一般将有阻塞操作设置特殊函数,在普通函数前加关键字async
async def get_request(url):print('正在请求的网址是:',url)time.sleep(2)print('请求网址结束!')return 123# 自定义一个回调函数(一般来做数据解析),给任务对象使用:必须有一个参数,用来获取特殊函数的返回值
def t_callback(t):#参数t就是任务对象data = t.result() #result()函数就可以返回特殊函数内部的返回值print('获取到特殊函数的返回值为:',data)# 创建协程对象
c = get_request('www.123.com')# 创建任务对象
task = asyncio.ensure_future(c)# 任务对象添加回调函数,事件对象启用的时候,特殊函数和回调函数按顺序执行
task.add_done_callback(t_callback)#创建事件循环对象
loop = asyncio.get_event_loop()
loop.run_until_complete(task)

多任务的协程

特殊函数内部,不可以出现不支持异步模块的代码,否则会中断整个异步效果,例如sleep,requests,可以通过执行程序来判断。具体操作如下:

添加多个任务需要使用:wait()函数,其他代码省略,看最后一步
'''
特殊函数
回调函数
协程对象
'''tasks = []
for url in urls:c = get_request(url)task = asyncio.ensure_future(c)task.add_done_callback(parse)tasks.append(task)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

await关键字:挂起发生阻塞操作的任务对象。在任务对象表示的操作中,凡是阻塞操作的前面都必须加上await关键字进行修饰,但不是所有阻塞操作都可以加await,需要添加支持协程的阻塞操作,await才会生效async def get_request(url):print('正在请求:',url)await asyncio.sleep(2)print('请求结束:',url)

完整代码

# 特殊函数先写出基本的网络请求框架,然后在每个with前面加async,每个阻塞操作前await,便于看懂和记忆
# 使用with是为了关闭协程,避免浪费资源
import asyncio
import time
from lxml import etree
import aiohttp
start = time.time()
urls = ['https://www.baidu.com','https://www.baidu.com','https://www.baidu.com'
]
#该任务是用来对指定url发起请求,获取响应数据
async def get_request(url):# requests是不支持异步的模块,所以加了await也没用# respOnse= await requests.get(url=url)#aiohttp是支持协程的网络请求,跟requests类似,创建请求对象(aiohttp_requests)async with aiohttp.ClientSession() as aiohttp_requests:#get请求,常用参数:url,headers,params,proxy#post请求,常用参数:url,headers,data,proxy#aiohttp处理代理的参数和requests不一样(注意),此处处理代理使用proxy='http://ip:port'async with await aiohttp_requests.get(url=url) as response:page_text = await response.text()#text():获取字符串形式的响应数据#read():获取二进制形式的响应数据await asyncio.sleep(2)return page_text
def call_back(t):#回调函数专门用于数据解析#获取任务对象请求到的页面源码数据page_text = t.result()tree = etree.HTML(page_text)a = tree.xpath('//a[1]/@href')print(a)tasks = []
for url in urls:c = get_request(url)task = asyncio.ensure_future(c)task.add_done_callback(call_back)tasks.append(task)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))print('总耗时:',time.time()-start)

uvloop加速

uvloop基于libuv,libuv是一个使用C语言实现的高性能异步I/O库,uvloop用来代替asyncio默认事件循环,可以进一步加快异步I/O操作的速度。 

import uvloop
loop = asyncio.get_event_loop()
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) # 在启用事件前加这一行代码即可
loop.run_until_complete(asyncio.wait(tasks))


推荐阅读
  • 访问一个网页的全过程
    准备:DHCPUDPIP和以太网启动主机,用一根以太网电缆连接到学校的以太网交换机,交换机又与学校的路由器相连.学校的这台路由器与一个ISP链接,此ISP(Intern ... [详细]
  • 在尝试使用C# Windows Forms客户端通过SignalR连接到ASP.NET服务器时,遇到了内部服务器错误(500)。本文将详细探讨问题的原因及解决方案。 ... [详细]
  • 在寻找轻量级Ruby Web框架的过程中,您可能会遇到Sinatra和Ramaze。两者都以简洁、轻便著称,但它们之间存在一些关键区别。本文将探讨这些差异,并提供详细的分析,帮助您做出最佳选择。 ... [详细]
  • 本文深入探讨了UNIX/Linux系统中的进程间通信(IPC)机制,包括消息传递、同步和共享内存等。详细介绍了管道(Pipe)、有名管道(FIFO)、Posix和System V消息队列、互斥锁与条件变量、读写锁、信号量以及共享内存的使用方法和应用场景。 ... [详细]
  • 深入浅出TensorFlow数据读写机制
    本文详细介绍TensorFlow中的数据读写操作,包括TFRecord文件的创建与读取,以及数据集(dataset)的相关概念和使用方法。 ... [详细]
  • Nginx 反向代理与负载均衡实验
    本实验旨在通过配置 Nginx 实现反向代理和负载均衡,确保从北京本地代理服务器访问上海的 Web 服务器时,能够依次显示红、黄、绿三种颜色页面以验证负载均衡效果。 ... [详细]
  • 本文介绍了在Java环境中使用PDFBox和XPDF工具从PDF文件中提取文本内容的方法。重点讨论了处理中文字符集及解决相关错误的技术细节,特别是针对某些特定格式的PDF文件(如网上填写的报名表和下载的论文)遇到的问题及解决方案。 ... [详细]
  • 深入解析Java枚举及其高级特性
    本文详细介绍了Java枚举的概念、语法、使用规则和应用场景,并探讨了其在实际编程中的高级应用。所有相关内容已收录于GitHub仓库[JavaLearningmanual](https://github.com/Ziphtracks/JavaLearningmanual),欢迎Star并持续关注。 ... [详细]
  • 本文介绍了如何使用JavaScript的Fetch API与Express服务器进行交互,涵盖了GET、POST、PUT和DELETE请求的实现,并展示了如何处理JSON响应。 ... [详细]
  • 本文探讨了如何在Classic ASP中实现与PHP的hash_hmac('SHA256', $message, pack('H*', $secret))函数等效的哈希生成方法。通过分析不同实现方式及其产生的差异,提供了一种使用Microsoft .NET Framework的解决方案。 ... [详细]
  • 优化SQL Server批量数据插入存储过程的实现
    本文介绍了一种改进的SQL Server存储过程,用于生成批量插入语句。该方法不仅提高了性能,还支持单行和多行模式,适用于SQL Server 2005及以上版本。 ... [详细]
  • KMP算法是处理字符串匹配的一种高效算法它首先用O(m)的时间对模板进行预处理,然后用O(n)的时间完成匹配。从渐进的意义上说,这样时间复 ... [详细]
  • 近期我们开发了一款包含天气预报功能的万年历应用,为了满足这一需求,团队花费数日时间精心打造并测试了一个稳定可靠的天气API接口,现正式对外开放。 ... [详细]
  • 本文探讨了SSDP(简单服务发现协议)和WSD(Web服务发现)协议,特别是SSDP如何通过固定多播地址239.255.255.250:1900实现局域网内的服务自发现功能。文中还详细介绍了SSDP协议的关键操作类型及其应用场景。 ... [详细]
  • 深入理解Vue.js:从入门到精通
    本文详细介绍了Vue.js的基础知识、安装方法、核心概念及实战案例,帮助开发者全面掌握这一流行的前端框架。 ... [详细]
author-avatar
enjoy楠神
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有