wordpress的站点地址和,wordpress 文章模板,建设银行的官方网站纪念币,2022推广app赚佣金平台协程 文章目录 协程协程的优劣势什么是IO密集型任务特点示例与 CPU 密集型任务的对比处理 I/O 密集型任务的方式总结 创建并使用协程asyncio模块 创建协程函数运行协程函数asyncio.run(main())aiohttp模块调用aiohttp模块步骤 aiofiles————协程异步函数遇到的问题一 await …协程 文章目录 协程协程的优劣势什么是IO密集型任务特点示例与 CPU 密集型任务的对比处理 I/O 密集型任务的方式总结 创建并使用协程asyncio模块 创建协程函数运行协程函数asyncio.run(main())aiohttp模块调用aiohttp模块步骤 aiofiles————协程异步函数遇到的问题一 await asyncio.wait(task)的作用1. **理解 await 的作用**2. **asyncio.wait(task) 的作用**3. **启动多个任务** 多协程案例——爬取图片完整代码设计思路按顺序解读代码主函数 亲爱的读者们 感谢你们访问我的博客如果你希望更深入、透彻地了解文章中的内容欢迎随时私信我。我可以为你录制视频讲解让知识变得更加生动有趣。
当然若你们对这个想法感兴趣的朋友们越多我会更有动力去制作这些视频哦让我们一起探索更多的知识共同成长
期待你的私信
协程(coroutine):也叫作微线程纤程协程是单线程下的并发。 协程的作用:在执行函数A时随时中断去执行函数B然后再中断函数B.返回来执行函数A该操作类似多线程但协程中只有一个线程在执行。 微观上:一个一个任务的切换切换条件一般是IO操作 宏观上:是多个任务在同时执行——多任务异步操作
协程的优劣势 协程的优势: 非常适用于I/O密集型任务 执行效率高(切换函数而不切换线程没有多余开销) 不需要锁机制。 协程的劣势 无法利用多核资源 进行阻塞操作会阻塞掉整个程序。
什么是IO密集型任务
I/O 密集型任务I/O-bound task是指那些在执行过程中主要受限于输入/输出I/O操作性能的任务。这类任务的执行效率往往取决于数据的读写速度而不是 CPU 处理能力。
特点
依赖于外部设备I/O 密集型任务常常涉及与外部设备的交互如硬盘、网络、数据库等。等待时间长由于 I/O 操作如文件读取、网络请求通常比 CPU 操作慢I/O 密集型任务在执行时可能会有较长的等待时间。CPU 空闲在等待 I/O 操作完成时CPU 可能处于空闲状态未能充分利用计算资源。
示例
文件读取/写入长时间读取或写入大文件。网络请求等待远程服务器的响应如 HTTP 请求。数据库操作查询或写入数据库的过程这些操作通常涉及网络通信和磁盘访问。
与 CPU 密集型任务的对比
I/O 密集型任务主要依赖于 I/O 操作CPU 通常在执行期间处于等待状态。CPU 密集型任务主要依赖于计算和处理通常会占用大量的 CPU 资源。
处理 I/O 密集型任务的方式
采用 异步编程如使用回调或 Promises 来处理 I/O 操作而不是阻塞主线程。利用 多线程或多进程同时处理多个 I/O 操作以减少整体等待时间。
总结
I/O 密集型任务是在执行中高度依赖于数据交换和处理速度的任务。理解 I/O 密集型任务的概念有助于在设计和优化系统时选择合适的编程模型和架构。
因为爬虫主要就是和网络进行交互不太消耗我们自己电脑的CPU负责接收网路返回的数据是一种写入操作就是适合爬虫
创建并使用协程
asyncio模块
python的标准库
创建协程函数 async def function() await IO操作调用协程函数 async def main() await function(并发执行多个协程: asyncio.create task(运行协程: asyncio.run(main())
import asyncio # 导入模块
import time# 创建协程函数
async def singing():print(start singing)time.sleep(2)print(end singing)async def dancing():print(start dance)time.sleep(3)print(end dance)async def main():# 调用协程函数await singing()await dancing()if __name__ __main__:start_time time.time()# 运行协程函数asyncio.run(main())end_time time.time()print(ftime:{end_time-start_time})start singing
end singing
start dance
end dance
time:5.002954006195068
发现是5秒就是串行上面的这个程序是协程程序但是是单协程也就是串行一个一个来
创建协程函数
asyncio.create_task(singing())
await asyncio.wait([task1,task2])
import asyncio # 导入模块
import time# 创建协程函数
async def singing():print(start singing)await asyncio.sleep(2)print(end singing)async def dancing():print(start dance)await asyncio.sleep(3)print(end dance)async def main():# 使用协程里面的任务函数来创建并发多协程task1 asyncio.create_task(singing())task2 asyncio.create_task(dancing())await asyncio.wait([task1,task2])if __name__ __main__:start_time time.time()# 运行协程函数asyncio.run(main())end_time time.time()print(ftime:{end_time-start_time})start singing
start dance
end singing
end dance
time:3.0030245780944824
和多线程一样都是有一个模块自己的函数来启动线程/协程time是一个同步函数不能让CPU异步执行即便是使用了并发执行多任务函数CPU也不能同时运行
使用await调用协程函数自带的sleep函数
CPU遇到await语句并且发生了堵塞比如网络请求还没到或者这个sleep等待函数未执行完CPU都会自己切换出去
运行协程函数asyncio.run(main())
import asyncio # 导入模块
import time# 创建协程函数
async def singing():print(start singing)await asyncio.sleep(2)print(end singing)async def dancing():print(start dance)await asyncio.sleep(3)print(end dance)async def main():# 和多线程一样可以把要启动的多协程任务放到一个列表里面最后在启动列表task []for i in range(3):task.append(asyncio.create_task(singing()))task.append(asyncio.create_task(dancing()))await asyncio.wait(task) # 启动协程 if __name__ __main__:start_time time.time()# 运行协程函数asyncio.run(main())end_time time.time()print(ftime:{end_time-start_time})start singing
start dance
start singing
start dance
start singing
start dance
end singing
end singing
end singing
end dance
end dance
end dance
time:3.003164768218994和多线程一样先是用asyncio模块下面的create task也就是字面意思创建一个多协程任务把协程函数写在里面
外面使用for循环控制循环次数也就是协程的数量每循环一次就会在这个create task里面多两个任务
所以循环了三次就有6个任务被创建
因为这是协程任务里面的sleep就是阻塞所以程序一直在切换协程
aiohttp模块
相当于异步requests模块适合在协程中使用使用方法类似 requests模块
官网:https://docs.aiohttp.org/en/stable/client_quickstart.html requests是同步模块在协程中不能及时的释放CPU所以需要使用协程专用的aiohttp模块
调用aiohttp模块步骤
async with 是关键字aiohttp.ClientSession() 这是一个方法然后起名字async with aiohttp.ClientSession() as session:async with session.get(url) as r: 前面两个是关键字后面的是请求方式和requests.get()一样返回方式有三种textcontentjson这里打印返回值的时候还要在后面加一个括号r.text()r.centent.read(),调用centent的时候还要加一个read()后缀r.json()
import asyncio
import aiohttpasync def downloads(url):# print(url)name url.split(/)[-1]# print(name)async with aiohttp.ClientSession() as session:async with session.get(url) as r: # 这里是把得到的网络请求内容赋值给rwith open(frE:\python笔记\{name},wb) as f:f.write(await r.content.read()) # 这里需要使用等待关键字因为是网络请求网络响应的速度比不上CPU运算的速度print(f已完成……{name})async def main():urls (https://i1.huishahe.com/uploads/allimg/202302/9999/bcc80e5d24.jpg,https://i1.huishahe.com/uploads/allimg/202302/9999/8ccff5510c.jpg,https://i1.huishahe.com/uploads/allimg/202302/9999/186094180f.jpg)task []for url in urls:task.append(asyncio.create_task(downloads(url))) # 把协程任务存进列表await asyncio.wait(task) # 启动列表内的任务并告诉协程这是一个等待语句if __name__ __main__:asyncio.run(main())设计思路
设计协程函数入口 把协程函数写入协程任务使用await 运行协程任务 设计协程函数 async with aiohttp.ClientSession() as session这个是aiohttp的方法必须要写async with session.get(url) as r 这个就是异步网络请求了返回内容在r里面
aiofiles————协程异步函数
也是协程里面的函数也是异步函数可以主动等待网络的返回值
import asyncio
from os import write
import aiofiles
import aiohttpasync def downloads(url):# print(url)name url.split(/)[-1]# print(name)async with aiohttp.ClientSession() as session:async with session.get(url) as r: # 这里是把得到的网络请求内容赋值给r# with open(frE:\python笔记\{name},wb) as f:# f.write(await r.content.read()) # 这里需要使用等待关键字因为是网络请求网络响应的速度比不上CPU运算的速度# print(f已完成……{name})async with aiofiles.open(frE:\python笔记\{name},wb) as f:await f.write(await r.content.read())print(f已完成……{name})和原来的文件保存方式相似就是换了个关键字
因为要使用协程函数所以要使用async关键字因为网络请求需要等待可能阻塞还要写await让程序挂起
遇到的问题
一 await asyncio.wait(task)的作用
在使用 Python 的 asyncio 库时协程的调度和执行是通过事件循环来处理的。await asyncio.wait(task) 是用来等待一组任务完成的关键工具。下面是它的重要性和工作原理的详细解释
1. 理解 await 的作用
await 用于暂停协程的执行直到被 await 的任务完成。它允许事件循环在等待的同时执行其他任务从而提高效率。在 async 函数中, 使用 await 的时候协程会被挂起控制权返回到事件循环从而允许其他协程运行。
2. asyncio.wait(task) 的作用
asyncio.wait 是一个并发等待工具它接受一个任务或任务列表直到这些任务中的一个或全部完成。它返回一个集合包含已完成和未完成的任务方便后续处理。通过等待任务的完成我们可以获得每个任务的结果或获取异常信息。
3. 启动多个任务
使用 asyncio.wait 启动和管理多个协程任务。例如当有多个协程需要并发执行时可以将它们封装在一个任务列表中然后调用 asyncio.wait 来调度。这样可以让我们更好地控制任务的执行以及处理结果和异常。
多协程案例——爬取图片
效果图 完整代码
import pprintimport aiofiles
import aiohttp
import asyncio# 使用BS4解析网页
from bs4 import BeautifulSoup
from requests.packages import target# 获取翻页地址
async def get_all_url(url):# print(url)async with aiohttp.ClientSession() as session:async with session.get(url) as r:# print(await r.text()) # 可等待对象-----网页源代码soup BeautifulSoup(await r.text(),html.parser)links soup.find(div,class_slist).find_all(a,target_blank) # 从目录页得到的总链接return_links []for link in links:return_links.append(link[href])return return_links# 获取目录页图片地址
async def get_url(url):# print(url)async with aiohttp.ClientSession() as session:async with session.get(url) as r:soup BeautifulSoup(await r.text(), html.parser)pic_address soup.find(div,class_photo-pic).find(img)[src]title soup.find(div,class_photo-pic).find(img)[title]# print(pic_address,title)return pic_address,title# 下载图片
async def download_pic(jpg_address,title):# print(jpg_address,title)async with aiohttp.ClientSession() as session:async with session.get(jpg_address) as r:# print(r) # okasync with aiofiles.open(frE:\python笔记\{title}.jpg,wb) as f:await f.write(await r.content.read())print(f已完成……{title})async def main():task1 []for i in range(1,2):if i 1:url https://www.moyublog.com/hdwallpapers/meinv/index.htmlelse:url fhttps://www.moyublog.com/hdwallpapers/meinv/index_{i}.htmltask1.append(asyncio.create_task(get_all_url(url)))dones,pending await asyncio.wait(task1) # await 关键字会返回两个内容第一个是在函数里面return的# for link in dones: # 这里的dones返回的是一个列表在改变目录页数的时候列表的数量也会改变# print(link.result()) # 当你发起异步操作时该操作可能尚未完成。任务对象在执行完成前只是一个占位符。只有在它们完成后调用 result() 才能获取实际的返回值。# 不调用 result() 直接打印任务对象会返回任务的信息如状态而不是实际的返回值。task2 []for link in dones:pages link.result() # 把一个列表储存在pages里面for rt in pages: # 这里是在使用rt读取一个pages列表最后打印出来# print(rt) # 发现得到网页正常task2.append(asyncio.create_task(get_url(rt)))dones,pending await asyncio.wait(task2)task3 []for t in dones:# print(t.result()) # 发现返回的内容都在元组里面jpg_address,title t.result() # 可以直接自定义两个参数接收元组里面的内容# print(jpg_address,title) # oktask3.append(asyncio.create_task(download_pic(jpg_address,title)))await asyncio.wait(task3)if __name__ __main__:asyncio.run(main())设计思路
找个比较合适的网站找个你感兴趣的分类获取目录页网址自己找规律组合其他的目录页获取目录页里面的每一种图片网址从图片的网址找到图片的链接下载并保存图片
按顺序解读代码
先要清楚这里是多协程代码会有网络请求等语句也就是程序堵塞的地方都要使用aio的关键字比如异步网络请求aiohttp异步文件储存aiofiles异步函数执行async异步函数的运行asyncio.wait还有异步任务的创建asyncio.creata_task()
找目录页里面的图片链接的时首先按照aiohttp.ClientSession()的方法创建session在使用sesssion.get的方式向网络发送请求这里的session.get的用法和requests.get一样在上一步已经把返回的内容储存在对象r里面了这里使用了BS4的语法进行文件解析也算是练习一下BS4的语法在这里获取到了图片页的链接
我们获得了图片页的链接和名称这个协程的任务就结束了现在我们把这两个内容返回出去
这是一个协程函数它的返回内容有两个dones和pending前者是已完成的内容后者是未完成的内容
想要正常获取协程函数的返回值我们需要使用这两个参数结束返回值后面的参数不用管
我们对前面的参数使用result()方法解析就可以正常获取内容了
这里我们得到了很多列表也就是一个目录页里面的所有链接在一个列表里面
把这个列表传递给下一个函数get_url这个函数的工作是专门从图片页的链接得到图片的jpg文件和名称
同样也是协程函数和上面的处理方式一样
再把图片的jpg链接和名称给下载及保存图片的函数这里就使用了异步文件保存的方式因为网络请求有一定的延迟还要使用await关键字
主函数
我们使用for循环构造目录页的链接并存入协程任务
然后是把链接给处理目录页的函数
再把返回内容给图片页函数
再把返回内容给下载图片的函数
这些都是异步协程操作也就是I/O密集型任务
所以可以在极短的时间保存大量的图片比之前的多线程快了很多
await 在 asyncio.wait 中并不会直接返回 dones 和 pendings 两个参数。相反await asyncio.wait(...) 返回的是一个包含两个集合的元组
done: 包含所有已完成的 Task 对象。pending: 包含所有未完成的 Task 对象。
研究的好几天才能较为熟练的使用协程函数就是比较麻烦但是因为是异步加载的请求所以可以不用在等待网络返回内容时一直占用CPU了CPU会去执行其他的协程