协程
- 参考网址
- https://www.cnblogs.com/wupeiqi/p/12834355.html
- 不是计算机提供,程序员人为创造
- 也称为微线程,是一种上下文切换技术(通过一个线程实现代码块互相切换执行)
- 普通代码的执行流程自上而下顺序执行
def fun1():
print(1)
# ...
print(2)
def fun2():
print(3)
# ...
print(4)
fun1()
fun2()
- 结果: 等fun1执行完,再执行fun2
-
实现方法
- greenlet: 早期模块
- yield 关键字
- asyncio装饰器(py3.4开始支持)
- async,await关键字(py3.5开始支持,推荐)
-
greenlet实现协程
from greenlet import greenlet
def fun1():
print(1)
gr2.switch() # 跳转到fun2
print(2)
gr2.switch()
def fun2():
print(3)
gr1.switch() # 跳转到fun1
print(4)
gr1 = greenlet(fun1)
gr2 = greenlet(fun2)
gr1.switch() # 开始执行
- 返回结果:
1
3
2
4
- yield示例
def fun1():
yield 1
yield from fun2() # 切换到 fun2
yield 2
def fun2():
yield 3
yield 4
f1 = fun1()
for item in f1:
print(item)
- 返回结果:
1
3
4
2
- asyncio示例
import asyncio
# 先定义两个协程对象
@asyncio.coroutine
def fun1():
print(1)
# 这里不能直接 asyncio.sleep(2)(报错信息: coroutine 'sleep' was never awaited)
yield from asyncio.sleep(2)
print(2)
@asyncio.coroutine
def fun2():
print(3)
yield from asyncio.sleep(2)
print(4)
# 创建任务
tasks = [
asyncio.ensure_future(fun1()),
asyncio.ensure_future(fun2())
]
# 创建'轮询对象'并执行任务
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
- async & await示例
import asyncio
async def fun1(): # 协程函数对象
print(1)
await asyncio.sleep(2) # 等的时候切换到其他代码块并执行
print(2)
async def fun2():
print(3)
await asyncio.sleep(2)
print(4)
tasks = [
asyncio.ensure_future(fun1()),
asyncio.ensure_future(fun2())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
- 结果:
1
3
2
4
非异步协程
下载图片示例
import requests
url_list = [
'https://www.aclas.com/uploadfile/uploadfile/225986195944098.png',
'https://www.aclas.com/uploadfile/uploadfile/26507715014500.jpg',
'https://www.aclas.com/uploadfile/uploadfile/34698673154118.jpg',
]
def download_image(url):
filename = url.rsplit('/')[-1]
res = requests.get(url)
with open(filename, 'wb') as file:
file.write(res.content)
print(filename + '下载完成')
if __name__ == '__main__':
for url in url_list:
download_image(url)
异步协程
下载图片示例
import aiohttp
import asyncio
url_list = [
'https://www.aclas.com/uploadfile/uploadfile/225986195944098.png',
'https://www.aclas.com/uploadfile/uploadfile/26507715014500.jpg',
'https://www.aclas.com/uploadfile/uploadfile/34698673154118.jpg',
]
# 下载图片
async def download_image(request,url):
print('开始发送请求',url)
async with request.get(url,verify_ssl=False) as res:
content = await res.content.read()
filename = url.rsplit('/')[-1]
with open(filename, 'wb') as file:
file.write(content)
print(filename + '下载完成')
# 创建请求对象并创建任务,等待任务执行完成
async def main():
async with aiohttp.ClientSession() as request:
tasks = [asyncio.create_task(download_image(request,url)) for url in url_list]
await asyncio.wait(tasks)
if __name__ == '__main__':
# asyncio.run(main()) 这种写法会报错...
loop = asyncio.get_event_loop() # 创建'轮询对象'并开始执行
loop.run_until_complete(main())
异步协程
- 参考网址
- https://www.cnblogs.com/wupeiqi/p/12834355.html
意义
- 计算型的操作,利用协程来回切换执行,没有任何意义,来回切换并保存状态 反倒会降低性能。
- IO型的操作,利用协程在IO等待时间就去切换执行其他任务,当IO操作结束后再自动回调,那么就会大大节省资源并提供性能,从而实现异步编程(不等待任务结束就可以去执行其他代码)。
底层原理,伪代码演示
- 事件循环,可以把他当做是一个while循环,这个while循环在周期性的运行并执行一些
任务
,在特定条件下终止循环
# 伪代码
任务列表 = [ 任务1, 任务2, 任务3,... ]
while True:
可执行的任务列表,已完成的任务列表 = 去任务列表中检查所有的任务,将'可执行'和'已完成'的任务返回
for 就绪任务 in 已准备就绪的任务列表:
执行已就绪的任务
for 已完成的任务 in 已完成的任务列表:
在任务列表中移除 已完成的任务
如果 任务列表 中的任务都已完成,则终止循环
开始
- 必须创建一个
轮询对象
import asyncio
loop = asyncio.get_event_loop() # 创建轮询对象
- 定义
协程函数
# 定义一个协程函数
async def func():
pass
# 调用协程函数,返回一个协程对象
# 注意事项: 调用协程函数时,函数内部代码不会执行,只是会返回一个协程对象
result = func()
- 基本应用:
事件循环
和协程对象
配合才能实现
import asyncio
async def func():
print("协程内部代码")
# 调用协程函数,返回一个协程对象。
result = func()
# 方式一
# loop = asyncio.get_event_loop() # 创建一个事件循环
# loop.run_until_complete(result) # 将协程当做任务提交到事件循环的任务列表中,协程执行完成之后终止。
# 方式二
# 本质上方式一是一样的,内部先 创建事件循环 然后执行 run_until_complete,一个简便的写法。
# asyncio.run 函数在 Python 3.7 中加入 asyncio 模块,
asyncio.run(result)
await关键字
: 是一个只能在协程函数中使用的关键字
- 用于遇到IO操作时挂起 当前协程(任务),当前协程(任务)挂起过程中 事件循环可以去执行其他的协程(任务),当前协程IO处理完成时,可以再次切换回来执行await之后的代码
import asyncio
async def func():
print("执行协程函数内部代码")
# 遇到IO操作挂起当前协程(任务),等IO操作完成之后再继续往下执行。
# 当前协程挂起时,事件循环可以去执行其他协程(任务)。
response = await asyncio.sleep(2)
print("IO请求结束,结果为:", response)
result = func()
asyncio.run(result)