Python并发编程库:Asyncio的异步编程实战

ops/2024/11/13 3:35:04/

Python并发编程库:Asyncio的异步编程实战

在现代应用中,并发和高效的I/O处理是影响系统性能的关键因素之一。Python的asyncio库是专为异步编程设计的模块,提供了一种更加高效、易读的并发编程方式,适用于处理大量的I/O密集型任务(如网络请求、文件操作等)。在这篇博客中,我们将详细介绍如何使用asyncio来进行异步编程,并通过一个实战案例,展示asyncio如何提升程序的性能。
在这里插入图片描述

1. 异步编程基础概念

在开始编码前,我们先理解一些基本概念:

  • 同步:任务按顺序依次执行,只有当前任务执行完成后,下一个任务才会开始执行。
  • 异步:任务可以并发执行,当遇到I/O操作时,程序可以切换到其他任务执行,从而不必等待。
  • 协程(Coroutine):协程是可以被挂起和恢复的函数,用于实现异步执行。在Python中,用async def定义协程函数。
  • 事件循环(Event Loop)asyncio的核心,它负责调度并运行协程,当协程遇到await时就会释放控制权,切换到其他任务。
    在这里插入图片描述

2. Asyncio的核心功能

asyncio库主要由以下几个核心部分组成:

  • 事件循环:管理所有异步任务的调度与执行。
  • 协程函数:用async def定义的函数,可以包含await关键字,表示程序可以在此处暂停并切换任务。
  • 任务(Tasks):将协程封装成任务,让它们在事件循环中并发运行。
  • Future对象:表示一个异步操作的最终结果。

2.1 异步协程函数

asyncio中,用async def定义的函数即为协程函数。协程函数只有在被await调用时才会执行。

python">import asyncioasync def my_coroutine():print("Start coroutine")await asyncio.sleep(1)print("End coroutine")# 运行协程
asyncio.run(my_coroutine())

2.2 任务的创建

可以使用asyncio.create_task将协程封装成任务,从而允许多个任务并发执行:

python">async def task1():print("Task 1 start")await asyncio.sleep(2)print("Task 1 end")async def task2():print("Task 2 start")await asyncio.sleep(1)print("Task 2 end")async def main():task_1 = asyncio.create_task(task1())task_2 = asyncio.create_task(task2())await task_1await task_2asyncio.run(main())

在上面的代码中,两个任务将并发执行。由于task2的延迟时间较短,因此它会先结束。

2.3 等待多个任务

asyncio.gather可以等待多个协程并发执行并返回结果:

python">async def fetch_data(n):print(f"Fetching data {n}")await asyncio.sleep(2)return f"Data {n}"async def main():results = await asyncio.gather(fetch_data(1), fetch_data(2), fetch_data(3))print(results)asyncio.run(main())

在这里,asyncio.gather会并发运行三个fetch_data任务,并返回所有任务的结果。
在这里插入图片描述

3. Asyncio异步编程实战

下面我们通过一个网络爬虫的例子展示asyncio的应用。假设我们需要从多个URL中提取数据,如果我们按顺序一个一个地请求这些URL,效率会非常低。我们可以使用asyncio并发请求这些URL,从而显著提升程序性能。

3.1 使用Asyncio实现简单网络爬虫

我们将使用aiohttp库实现异步的HTTP请求。aiohttp是一个支持异步的HTTP客户端,非常适合和asyncio结合使用。

首先,安装aiohttp库:

pip install aiohttp

然后,我们编写异步爬虫代码:

python">import asyncio
import aiohttp# 异步获取单个URL数据
async def fetch_url(session, url):async with session.get(url) as response:return await response.text()# 主函数:使用asyncio.gather并发请求多个URL
async def main(urls):async with aiohttp.ClientSession() as session:tasks = [fetch_url(session, url) for url in urls]results = await asyncio.gather(*tasks)return results# 示例URL列表
urls = ["http://example.com","http://example.org","http://example.net"
]# 运行主函数并获取结果
data = asyncio.run(main(urls))
for i, content in enumerate(data):print(f"Content of URL {i+1}:")print(content[:100])  # 打印前100个字符

在这个代码中,我们并发地请求了多个URL,并获取每个URL的内容。这样做的好处是,程序可以在等待一个URL响应时去处理其他URL请求,极大地提高了效率。

3.2 超时控制与错误处理

在网络请求中,超时和错误处理也是重要的一部分。我们可以为fetch_url添加超时和异常处理,以确保程序在遇到问题时不会崩溃。

python">async def fetch_url(session, url):try:async with session.get(url, timeout=5) as response:response.raise_for_status()  # 检查响应状态return await response.text()except asyncio.TimeoutError:print(f"Timeout error for URL: {url}")except aiohttp.ClientError as e:print(f"Error fetching URL {url}: {e}")return None  # 返回None表示请求失败

在添加了错误处理后,即使某些URL请求失败,程序也会继续执行。
在这里插入图片描述

4. 性能对比:同步 vs 异步

为了更直观地感受asyncio带来的性能提升,我们可以通过对比同步和异步爬虫的执行时间。

4.1 同步版本爬虫

python">import requests
import timedef fetch_url_sync(url):response = requests.get(url)return response.text# 同步爬虫主函数
def main_sync(urls):results = []for url in urls:results.append(fetch_url_sync(url))return results# 测试同步爬虫
start_time = time.time()
data_sync = main_sync(urls)
end_time = time.time()print(f"同步爬虫耗时: {end_time - start_time} 秒")

4.2 异步版本爬虫

直接运行我们上面的异步爬虫,并计算其执行时间:

python">start_time = time.time()
data_async = asyncio.run(main(urls))
end_time = time.time()print(f"异步爬虫耗时: {end_time - start_time} 秒")

在多个URL请求的场景下,异步爬虫的执行时间通常会比同步爬虫短得多,这展示了asyncio在I/O密集型任务中的显著优势。
在这里插入图片描述

5. 基础总结

上面介绍了asyncio的基本概念及其在Python异步编程中的应用,通过代码实例展示了如何使用asyncio进行异步操作以及如何显著提高程序的并发能力。异步编程虽然学习曲线较高,但在I/O密集型任务中具有明显优势,尤其是在网络请求、文件处理等场景中。
在这里插入图片描述

6. 进阶应用:使用信号量和限制并发数量

在实际应用中,异步任务的数量可能非常多(例如几百或几千个URL请求)。如果全部并发执行,可能会导致系统资源耗尽,甚至触发对方服务器的访问限制。asyncio提供了Semaphore(信号量)机制,可以限制同时执行的任务数量。

下面是如何使用信号量来限制并发任务数的示例:

python">async def fetch_url_with_semaphore(semaphore, session, url):async with semaphore:  # 使用信号量来限制并发数量try:async with session.get(url, timeout=5) as response:return await response.text()except Exception as e:print(f"Error fetching {url}: {e}")return Noneasync def main_with_semaphore(urls, max_concurrent_tasks=5):semaphore = asyncio.Semaphore(max_concurrent_tasks)  # 限制并发数量async with aiohttp.ClientSession() as session:tasks = [fetch_url_with_semaphore(semaphore, session, url) for url in urls]results = await asyncio.gather(*tasks)return results# 设置最大并发任务数为5
start_time = time.time()
data_with_limit = asyncio.run(main_with_semaphore(urls, max_concurrent_tasks=5))
end_time = time.time()
print(f"使用信号量限制的异步爬虫耗时: {end_time - start_time} 秒")

在这个例子中,我们通过信号量控制了最多只有5个任务同时运行,从而有效管理了系统资源的使用。
在这里插入图片描述

7. 异步上下文管理器

异步编程中,我们经常需要创建和关闭连接、打开和关闭文件等,这些操作通常需要使用上下文管理器。Python 3.5引入了异步上下文管理器,允许我们用async with来管理异步资源。以aiohttp的Session为例,在异步编程中,这样的上下文管理器能够自动处理连接的关闭,非常方便。

使用异步上下文管理器读取文件

如果需要异步地处理文件操作,可以使用aiofiles库,该库支持异步读取和写入文件。以下是一个读取文件的简单示例:

首先安装aiofiles库:

pip install aiofiles

然后在代码中使用它:

python">import aiofilesasync def read_file_async(file_path):async with aiofiles.open(file_path, mode='r') as file:content = await file.read()return content# 示例
async def main():content = await read_file_async("example.txt")print(content)asyncio.run(main())

使用异步文件操作在处理大文件或需要高并发的文件操作时非常有用,因为它不会阻塞事件循环。
在这里插入图片描述

8. 小结

asyncio提供了强大的异步编程能力,使得Python在处理I/O密集型任务时的效率得到了显著提升。通过本文介绍的实战示例,你已经掌握了asyncio的核心概念和一些常用技术,包括:

  • 如何定义和运行协程函数
  • 如何并发地执行多个任务
  • 使用asyncio.gather批量并发执行任务
  • 利用信号量来控制并发任务数量
  • 应用异步上下文管理器管理资源

asyncio不仅适用于网络请求和文件操作,也可以应用于多种场景,例如爬虫、聊天应用、数据采集等。掌握asyncio之后,你会发现Python的异步编程能够使程序更加高效、流畅,从而提升系统的整体性能。希望你能在实际项目中将这些技术加以应用,打造更高效的异步系统。
在这里插入图片描述


http://www.ppmy.cn/ops/132162.html

相关文章

How to use ffmpeg to convert video format from .webm to .mp4

The .mp4 container format doesn’t support the VP8 codec, which is commonly used in .webm files. MP4 containers typically use the H.264 codec for video and AAC for audio. You’ll need to re-encode the video using the H.264 codec and re-encode the audio us…

uniapp组件样式运行至小程序失效

文章目录 一、uniapp样式穿透打包运行至微信小程序失效 一、uniapp样式穿透打包运行至微信小程序失效 组件样式隔离文章参考 解决方案 options: {styleIsolation: "shared",},这个配置项改变了小程序组件的样式隔离模式,使得组件的样式能够共享和继承。…

AscendC从入门到精通系列(一)初步感知AscendC

1 什么是AscendC Ascend C是CANN针对算子开发场景推出的编程语言,原生支持C和C标准规范,兼具开发效率和运行性能。基于Ascend C编写的算子程序,通过编译器编译和运行时调度,运行在昇腾AI处理器上。使用Ascend C,开发者…

Java打造智能语音陪聊软件?提升用户体验的新路径

在现在的日常生活中,大家做什么都会寻找一个“搭子”,例如“游戏搭子”,很多时候一时半会找不到就会很苦恼,就因此诞生了语音陪聊软件。然而Java作为一种广泛使用的编程语言,在开发高效、稳定的应用程序方面具有显著优…

mysql if函数如何处理无匹配记录的情况?使用聚合函数

问题描述:编者在使用mysql中的if(car_number,"监管车辆","非监管车辆")函数时,场景为在一个car表中如果能查到具体某辆车这辆车就是我司监管车辆,差不到就不是我司监管车辆显示非监管车辆,遇到匹配不到的数据…

基于ASP.NET+SQL Server实现简单小说网站(包括PC版本和移动版本)

一、网站简介 1.1 设计思路 根据一般人阅读小说的顺序,利用了HTML5、CSS3制作一个普通pc端和跨平台移动端。 PC端:小说的首页、小说某类具体信息、某小说详细信息页移动端:小说的首页、小说分类、小说某类具体信息、小说详情 1.2 网站的主…

如何使用 Puppeteer 和 Browserless 抓取亚马逊产品数据?

您可以在亚马逊上找到所有有关产品、卖家、评论、评分、特价、新闻等的相关且有价值的信息。无论是卖家进行市场调研还是个人收集数据,使用高质量、便捷且快速的工具将极大地帮助您准确地抓取亚马逊上的各种信息。 为什么抓取亚马逊产品数据很重要? 亚…

深入探索 Seaborn:高级绘图的艺术与实践

引言 在数据科学领域,数据可视化是至关重要的一步。它不仅能够帮助我们更好地理解数据,还能有效地传达信息,支持决策过程。Seaborn 是一个基于 Matplotlib 的高级 Python 数据可视化库,它提供了许多高级绘图功能,使得…