热门标签 | 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))


推荐阅读
  • 掌握DSP必备的56个核心问题,我已经将其收藏以备不时之需! ... [详细]
  • 本文提供了 RabbitMQ 3.7 的快速上手指南,详细介绍了环境搭建、生产者和消费者的配置与使用。通过官方教程的指引,读者可以轻松完成初步测试和实践,快速掌握 RabbitMQ 的核心功能和基本操作。 ... [详细]
  • Go语言实现Redis客户端与服务器的交互机制深入解析
    在前文对Godis v1.0版本的基础功能进行了详细介绍后,本文将重点探讨如何实现客户端与服务器之间的交互机制。通过具体代码实现,使客户端与服务器能够顺利通信,赋予项目实际运行的能力。本文将详细解析Go语言在实现这一过程中的关键技术和实现细节,帮助读者深入了解Redis客户端与服务器的交互原理。 ... [详细]
  • MongoDB Aggregates.group() 方法详解与编程实例 ... [详细]
  • 在 Linux 系统中,`/proc` 目录实现了一种特殊的文件系统,称为 proc 文件系统。与传统的文件系统不同,proc 文件系统主要用于提供内核和进程信息的动态视图,通过文件和目录的形式呈现。这些信息包括系统状态、进程细节以及各种内核参数,为系统管理员和开发者提供了强大的诊断和调试工具。此外,proc 文件系统还支持实时读取和修改某些内核参数,增强了系统的灵活性和可配置性。 ... [详细]
  • 深入解析零拷贝技术(Zerocopy)及其应用优势
    零拷贝技术(Zero-copy)是Netty框架中的一个关键特性,其核心在于减少数据在操作系统内核与用户空间之间的传输次数。通过避免不必要的内存复制操作,零拷贝显著提高了数据传输的效率和性能。本文将深入探讨零拷贝的工作原理及其在实际应用中的优势,包括降低CPU负载、减少内存带宽消耗以及提高系统吞吐量等方面。 ... [详细]
  • 在启用分层编译的情况下,即时编译器(JIT)的触发条件涉及多个因素,包括方法调用频率、代码复杂度和运行时性能数据。本文将详细解析这些条件,并探讨分层编译如何优化JVM的执行效率。 ... [详细]
  • Windows环境下详细教程:如何搭建Git服务
    Windows环境下详细教程:如何搭建Git服务 ... [详细]
  • 在Python 3环境中,当无法连接互联网时,可以通过下载离线模块包来实现模块的安装。具体步骤包括:首先从PyPI网站下载所需的模块包,然后将其传输到目标环境,并使用`pip install`命令进行本地安装。此方法不仅适用于单个模块,还支持依赖项的批量安装,确保开发环境的完整性和一致性。 ... [详细]
  • 深入解析Spring框架中的双亲委派机制突破方法
    在探讨Spring框架中突破双亲委派机制的方法之前,首先需要了解类加载器的基本概念。类加载器负责将类的全限定名转换为对应的二进制字节流。每个类在被特定的类加载器加载后,其唯一性得到保证。然而,这种机制在某些场景下可能会限制灵活性,因此Spring框架提供了一些策略来突破这一限制,以实现更加动态和灵活的类加载。这些策略不仅能够提升系统的可扩展性,还能在复杂的运行环境中确保类的正确加载和管理。 ... [详细]
  • ZeroMQ在云计算环境下的高效消息传递库第四章学习心得
    本章节深入探讨了ZeroMQ在云计算环境中的高效消息传递机制,涵盖客户端请求-响应模式、最近最少使用(LRU)队列、心跳检测、面向服务的队列、基于磁盘的离线队列以及主从备份服务等关键技术。此外,还介绍了无中间件的请求-响应架构,强调了这些技术在提升系统性能和可靠性方面的应用价值。个人理解方面,ZeroMQ通过这些机制有效解决了分布式系统中常见的通信延迟和数据一致性问题。 ... [详细]
  • Docker网络基础探讨了如何通过高效的技术手段实现跨主机容器间的顺畅通信与访问。本文深入分析了Docker网络架构,特别是其在多主机环境下的应用,为Go语言开发者提供了宝贵的实践指导和理论支持。 ... [详细]
  • 使用PyQt5与OpenCV实现电脑摄像头的图像捕捉功能
    本文介绍了如何使用Python中的PyQt5和OpenCV库来实现电脑摄像头的图像捕捉功能。通过结合这两个强大的工具,用户可以轻松地打开摄像头并进行实时图像采集和处理。代码示例展示了如何初始化摄像头、捕获图像并将其显示在PyQt5的图形界面中。此外,还提供了详细的步骤说明和代码注释,帮助开发者快速上手并实现相关功能。 ... [详细]
  • 如何使用 org.geomajas.configuration.FontStyleInfo.getColor() 方法及其代码示例详解 ... [详细]
  • 西北工业大学作为陕西省三所985和211高校之一,虽然在农业和林业领域不如某些顶尖院校,但在航空航天领域的实力尤为突出。该校的计算机科学专业在科研和教学方面也具有显著优势,是考研的理想选择。 ... [详细]
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社区 版权所有