一 阻塞和协程
在处理多个任务时,CPU不为当前的任务进行工作,此时就出现了阻塞状态。
一般当程序处于IO操作时,都会出现阻塞,比如input()
和requests.get()
,前者是等待输入数据,后者是等待对面服务器的响应反馈结果。
协程就是当程序遇见了IO操作时,选择性的切换到其他任务上,让CPU不停止工作。
在微观上体现为遇见IO后的一个任务一个任务的切换,多任务异步操作;
在宏观上观察为多个任务是一起执行。
二 协程的两种写法
第一种方法:
import asyncio
import time
async def func1():
print("你好,我是野比大雄")
# 异步操作,并挂起,让CPU执行其他任务
await asyncio.sleep(3)
print("你好,我是野比大雄")
async def func2():
print("你好,我是哆啦A梦")
await asyncio.sleep(2)
print("你好,我是哆啦A梦")
async def func3():
print("你好,我是源静香")
await asyncio.sleep(4)
print("你好,我是源静香")
if __name__ == '__main__':
f1 = func1()
f2 = func2()
f3 = func3()
tasks = [
f1,f2,f3
]
# 协程前计时
t1 = time.time()
# (协程)一次性启动多个任务
asyncio.run(asyncio.wait(tasks))
# 任务执行完成后计时
t2 = time.time()
# 总共耗时
print(t2 -t1)
第二种写法:
import asyncio
import time
async def func1():
print("你好,我是野比大雄")
await asyncio.sleep(3)
print("你好,我是野比大雄")
async def func2():
print("你好,我是哆啦A梦")
await asyncio.sleep(2)
print("你好,我是哆啦A梦")
async def func3():
print("你好,我是源静香")
await asyncio.sleep(4)
print("你好,我是源静香")
async def main():
tasks = [
func1(),
func2(),
func3()
]
await asyncio.wait(tasks)
if __name__ == '__main__':
# 协程前计时
t1 = time.time()
# (协程)一次性启动多个任务
asyncio.run(main())
# 任务执行完成后计时
t2 = time.time()
# 总共耗时
print(t2 -t1)
三 协程的简单应用
import asyncio
import time
async def download(url):
print("准备开始下载。。。")
# 模拟网络请求
await asyncio.sleep(3)
print("下载完毕!!!")
async def main():
urls = [
"https://www.bilibili.com/",
"http://www.baidu.com",
"https://www.youkuaiyun.com/"
]
tasks = []
for url in urls:
d = download(url)
tasks.append(d)
await asyncio.wait(tasks)
if __name__ == '__main__':
# 协程前计时
t1 = time.time()
# (协程)一次性启动多个任务
asyncio.run(main())
# 任务执行完成后计时
t2 = time.time()
# 总共耗时
print(t2 - t1)
四 实战案例
这一节需要用到一个新的模块,要导入aiohttp
模块,必须先安装这个模块。
安装完成后,会有提示信息。
在获取相关内容的基本信息后,我们就准备写代码,代码如下:
import asyncio
import aiohttp
urls = [
"http://kr.shanghai-jiuxin.com/file/bizhi/20220927/ortyhsuzqrb.jpg",
"http://kr.shanghai-jiuxin.com/file/bizhi/20220927/mow1dr2kbce.jpg",
"http://kr.shanghai-jiuxin.com/file/bizhi/20220927/ued4qp5cj2x.jpg"
]
async def aiodownload(url):
# 从右往左切2刀,得到[-1]位置的内容;而split()则从左往右切
name = url.rsplit("/",2)[-1]
# 发送requests请求
async with aiohttp.ClientSession() as session:
# 等价于 resp = requests.get()
async with session.get(url) as resp:
# 得到请求响应,写入文件,创建文件
with open(name,mode="wb") as f:
# 读取内容的程序是异步的,需要await挂起
f.write(await resp.content.read())
print(name, "下载完成!!!")
async def main():
tasks = []
for url in urls:
tasks.append(aiodownload(url))
await asyncio.wait(tasks)
if __name__ == '__main__':
asyncio.run(main())