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

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调用时才会执行。

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

2.2 任务的创建

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

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可以等待多个协程并发执行并返回结果:

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

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

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添加超时和异常处理,以确保程序在遇到问题时不会崩溃。

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 同步版本爬虫

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 异步版本爬虫

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

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(信号量)机制,可以限制同时执行的任务数量。

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

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

然后在代码中使用它:

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.mzph.cn/pingmian/59125.shtml

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

相关文章

Golang--数组、切片、映射

1、数组 1.1 数组类型 var 数组名 [数组大小]数据类型 package main import "fmt"func main(){//1、定义一个数组var arr1 [5]intarr1[0] 100arr1[1] 200fmt.Println(arr1) //[100 200 0 0 0] } 1.2 数组的初始化方式 package main import "fmt" func …

在VS中安装chatGPT

2、在VSCode中打开插件窗口 3、输入ChatGPT 4、这里有个ChatGPT中文版,就它了 5、安装 6、这时候侧边栏多了一个chatGPT分页图标,点击它 7、打个招呼 8、好像不行 9、看一下细节描述 10、根据要求按下按下快捷键 Ctrl Shift P 11、切换成国内模式 12、…

Linux下的Debugfs

debugfs 1. 简介 类似sysfs、procfs,debugfs 也是一种内存文件系统。不过不同于sysfs一个kobject对应一个文件,procfs和进程相关的特性,debugfs的灵活度很大,可以根据需求对指定的变量进行导出并提供读写接口。debugfs又是一个Li…

Fooocus图像生成软件本地部署教程:在Windows上快速上手AI创作

文章目录 前言1. 本地部署Fooocus图像生成软件1.1 安装方式1.2 功能介绍 2. 公网远程访问Fooocus3. 固定Fooocus公网地址 前言 本篇文章将介绍如何在本地Windows11电脑部署开源AI生图软件Fooocus,并结合Cpolar内网穿透工具轻松实现公网环境远程访问与使用。 Foooc…

修改HarmonyOS鸿蒙图标和名字,打包后安装到真机,应用图标丢失变成透明,修改名字也不生效,还是默认的labeL解决方案教程

HarmonyOS鸿蒙打包hap 安装应用到桌面没有图标,用hdc安装到真机,打包后应用图标丢失变成透明,名字也还是默认的label的bug,以下是解决方案 以下是修改方案: 1、修改应用名字: 2、修改应用图标&#xff1a…

Python小游戏20——超级玛丽

首先,你需要确保你的Python环境中安装了pygame库。如果还没有安装,可以使用以下命令进行安装: bash pip install pygame 运行效果展示 代码展示 python import pygame import sys # 初始化pygame pygame.init() # 设置屏幕尺寸 screen_width …

从富文本窥探苹果的代码秘密

从富文本窥探苹果的代码秘密 背景 在我们的业务场景下,为突出诸如 “利益点”和“利率” 等特性以推动订单成交,引入了 “富文本” 这一概念。富文本具备丰富格式的文本展示与编辑功能。然而,恰是由于富文本具有 “多样式”“复杂排版” 等特…

openstack之guardian介绍与实例创建过程

运行特征 采集模块:扩展Ceilometer,采集存储网、业务网连通性、nova目录是否可读写; 收集模块:将采集到的数据存储到数据库中; 分析模块:根据采集的结果,分析各节点状态,并进行反向检…

AVLTree

1.AVL树的概念 二叉搜索树虽然可以提高查找的效率,但是如果数据有序或者接近有序,二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。为了解决该问题,于是就有了AVLTree。即当向二叉搜索树中插入…

【数据结构二叉树】C非递归算法实现二叉树的先序、中序、后序遍历

引言: 遍历二叉树:指按某条搜索路径巡访二叉树中每个结点,使得每个结点均被访问一次,而且仅被访问一次。 除了层次遍历外,二叉树有三个重要的遍历方法:先序遍历、中序遍历、后序遍历。 1、递归算法实现先序、中序、后…

深入学习 Scrapy 框架:从入门到精通的全面指南

深入学习 Scrapy 框架:从入门到精通的全面指南 引言 在数据驱动的时代,网络爬虫成为了获取信息的重要工具。Scrapy 是一个强大的 Python 爬虫框架,专为快速高效地提取网页数据而设计。本文将深入探讨 Scrapy 的使用,从基础知识到…

蓝桥杯 区间移位--二分、枚举

题目 代码 #include <stdio.h> #include <string.h> #include <vector> #include <algorithm> #include <iostream> using namespace std; struct node{ int a,b; }; vector<node> q; bool cmp(node x,node y){ return x.b <…

SpringBoot+VUE2完成WebSocket聊天(数据入库)

下载依赖 <!-- websocket --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><!-- MybatisPlus --><dependency><groupId>com.ba…

图文深入介绍Oracle DB link(一)

1. 引言&#xff1a; 本文图文深入介绍Oracle DB link&#xff0c;先介绍基本概念。 2.DB link的定义 数据库链接&#xff08;Database Link&#xff0c;简称 DB Link&#xff09;是 Oracle 数据库中的一个重要功能。它是一种在一个 Oracle 数据库实例中访问另一个 Oracle 数…

MoonBit 双周报 Vol.59:新增编译器常量支持,改进未使用警告,支持跨包函数导入...多个关键技术持续优化中!

2024-11-04 MoonBit更新 增加了编译期常量的支持。常量的名字以大写字母开头&#xff0c;用语法 const C ... 声明。常量的类型必须是内建的数字类型或 String。常量可以当作普通的值使用&#xff0c;也可以用于模式匹配。常量的值目前只能是字面量&#xff1a; const MIN_…

HTB:Shocker[WriteUP]

目录 连接至HTB服务器并启动靶机 1.How many TCP ports are listening on Shocker? 使用nmap对靶机TCP端口进行开放扫描 2.What is the name of the directory available on the webserver that is a standard name known for running scripts via the Common Gateway Int…

力扣——另一个的子树(C语言)

1.题目&#xff1a; 给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree …

【C++】对左值引用右值引用的深入理解(右值引用与移动语义)

&#x1f308; 个人主页&#xff1a;谁在夜里看海. &#x1f525; 个人专栏&#xff1a;《C系列》《Linux系列》 ⛰️ 天高地阔&#xff0c;欲往观之。 ​ 目录 前言&#xff1a;对引用的底层理解 一、左值与右值 提问&#xff1a;左值在左&#xff0c;右值在右&#xff1f;…

解决 ClickHouse 高可用集群中 VRID 冲突问题:基于 chproxy 和 keepalived 的实践分析

Part1背景描述 近期&#xff0c;我们部署了两套 ClickHouse 生产集群&#xff0c;分别位于同城的两个数据中心。这两套集群的数据保持一致&#xff0c;以便在一个数据中心发生故障时&#xff0c;能够迅速切换应用至另一个数据中心的 ClickHouse 实例&#xff0c;确保服务连续性…

B2C电商平台如何提升转化率 小程序商城如何做好运营

在竞争激烈的电商市场中&#xff0c;提升转化率是每个B2C电商平台的重要目标。转化率直接影响销售业绩和盈利能力&#xff0c;因此&#xff0c;了解如何优化用户体验、增强客户信任和提高购买动机是至关重要的。商淘云分享一些有效的策略&#xff0c;帮助B2C电商平台提升转化率…