第一种描述:协程是一种可以被挂起,并且在挂起之后,还可以恢复执行的函数。
第二种描述:协程又称微线程,是一种用户态内的上下文切换技术。


协程对象
1.协程函数(coroutineFunction):使用『async关键字』修饰的函数,就是协程函数。
2.协程对象(coroutine Object):调用[协程函数』,就会得到[协程对象』。
注意:调用『协程函数』,并不会执行『协程函数』中的代码。
python
import asyncio
# 定义一个协程函数
async def work():
print('work开始')
print('working')
print('work结束')
return '工作结束'
# 调用协程函数,会得到协程对象
coroutine_obj=work()
# asyncio.run方法做了3件事:
# 1.创建一个事件循环。
# 2.将收到的协程对象,包装成一个任务(task),交给事件循环。
# 3.启动事件循环。
# 注意:asyncio.run会阻塞当前线程,直到任务执行完毕,并返回该任务return的最终结果。
res=asyncio.run(coroutine_obj)
print(res)
await关键字
- 挂起:await会暂停当前协程的执行。
2.等待:遇到await关键字,事件循环会立即安排await后面的对象去执行,并等待该对象执行完成,并且可以拿到执行结果。
关键点:在执行await后面的对象时,会出现两种情况:
情况一:如果在执行该对象中的代码时,遇到了【awaitI/0操作】(需要等待外部资源返回结果的操作)
例如:网络请求、文件读写等。
那CPu的控制权就会交给事件循环。
事件循环会去调度循环中的其他任务(如果有的话)
pythonimport asyncio async def work(): print('work开始') print('working') await asyncio.sleep(2) print('work结束') return '工作结束' async def main(): print('main开始') res=await work() print(res) print('main_ing') print('main结束') return 'main结束' # 调用协程函数,会得到协程对象 coroutine_obj=main() res=asyncio.run(coroutine_obj) print(res)情况二:如果该对象中的代码,不包含任何【awaitI/0操作】
例如:print打印、数学计算、逻辑计算等.
此时事件循环拿不到cPu控制权,无法调度循环中的其他任务,不会发生任务切换。
pythonimport asyncio async def work(): print('work开始') print('working') print('work结束') return '工作结束' async def main(): print('main开始') await work() print('main_ing') print('main结束') return 'main结束' # 调用协程函数,会得到协程对象 coroutine_obj=main() res=asyncio.run(coroutine_obj) print(res)
3.恢复:当await后的对象执行完毕,事件循环会恢复之前被挂起的协程,该协程会从当时挂起的位置继续执行,并拿到返回值
注意点:
await后面只能写『可等待的对象』,常见的可等待对象:1.协程对象、2.Future对象、3.Task对象
多个任务_同步执行
python
import asyncio
async def work(n,delay):
print(f'work{n}开始')
print(f'{n}working')
await asyncio.sleep(delay)
print(f'work{n}结束')
return f'{n}工作结束'
async def main():
print('main开始')
# 调用三次work函数,分别得到三个协程对象
coroutine1= work(1,2)
coroutine2= work(2,2)
coroutine3= work(3,2)
# 此处会等待coroutine1执行完成
res1=await coroutine1
print(res1)
# coroutine1完成后,再等待coroutine2完成
res2=await coroutine2
print(res2)
# coroutine2完成后,再等待coroutine3完成
res3=await coroutine3
print(res3)
print('main结束')
return 'main的返回值'
# 将协程对象交给事件循环
res=asyncio.run(main())
print(res)
多个任务_异步执行
python
import asyncio
import time
async def work(n,delay):
print(f'work{n}开始')
print(f'{n}working')
await asyncio.sleep(delay)
print(f'work{n}结束')
return f'{n}工作结束'
async def main():
print('main开始')
start=time.time()
# asyncio.create_task 会把一个协程对象包装成一个可被事件循环调度的任务,并注册到事件循环中
# 简写
res=await asyncio.gather(work(1,2),work(2,2),work(3,2))
print(res)
# task1= asyncio.create_task(work(1,2))
# task2= asyncio.create_task(work(2,2))
# task3= asyncio.create_task(work(3,2))
# 此处会等待task1执行完成
# res1=await task1
# print(res1)
# task1完成后,再等待task2完成
# res2=await task2
# print(res2)
# task2完成后,再等待task3完成
# res3=await task3
# print(res3)
print('main结束',time.time()-start)
return 'main的返回值'
# 将协程对象交给事件循环
res=asyncio.run(main())
print(res)
案例-下载图片
python
import requests
import aiohttp
import asyncio
# 协程
async def download_picture(session,url):
print(f'开始下载:{ur1}')
# 发送网络请求,获取图片,等待的过程是IO等待
res=await session.get(url)
# 等待数据
cont=await res.read()
with open(url[-10:],'wb') as file:
file.write(cont)
# 释放连接资源
await res.release()
print('下载并保存完毕')
async def main():
url_list = [
"",
"",
""
]
# 创建会话对象(发送请求的工具)
session=aiohttp.ClientSession()
coroutine_list=[download_picture(session,url) for url in url_list]
await asyncio.gather(*coroutine_list)
# 关闭会话
await session.close()
asyncio.run(main())
# 传统
# def download_picture(url):
# print(f'开始下载:{ur1}')
# #发送网络请求,获取这张图片
# response = requests.get(url)
# # 保存图片到本地
# with open(url[-10:],'wb') as file:
# file.write(response.content)
# print('下载并保存完毕')
# def main():
# url_list = [
# "",
# "",
# ""
# ]
# for url in url_list:
# download_picture(url)
# main()