一、什么是协程及实现方式
1.1 协程
又称微线程,纤程,也称为用户级线程,在不开辟线程的基础上完成多任务,也就是在单线程的情况下完成多任务,多个任务按照一定顺序交替执行。
1.2 实现方式
- greenlet,早期模块;
- yield关键字;
- asyncio装饰器 (py3.4);
- async、await关键字 (py3.5)(推荐);
二、实现方式示例
2.1 greenlet
from greenlet import greenlet
def func1():
print(1) # 第1步: 输出 1
gr2.switch() # 第3步: 切换到 func2 函数
print(2) # 第6步: 输出 2
gr2.switch() # 第7步: 切换到 func2 函数,从上一次执行的位置继续向后执行
def func2() :
print(3) # 第4步: 输出 3
grl.switch() # 第5步: 切换到 func1 函数,从上一次执行的位置继续向后执行
print(4) # 第8步: 输出 4
gr1 = greenTet(func1)
gr2 = greenTet(func2)
grl.switch() # 第1步: 去执行 func1 函数
2.2 yield关键字
import time
def work1():
while True:
print("----work1---")
yield
time.sleep(0.5)
def work2():
while True:
print("----work2---")
yield
time.sleep(0.5)
def main():
w1 = work1()
w2 = work2()
while True:
next(w1)
next(w2)
if __name__ == "__main__":
main()
2.3 asyncio装饰器(py3.4)
import asyncio
@asyncio.coroutine
def func1():
print(1)
yield from asyncio.sleep(2) # 遇到IO耗时操作,自动化切换到tasks中的其他任务
print(2)
@asyncio.coroutine
def func2():
print(3)
yield from asyncio.sleep(2) # 遇到IO耗时操作,自动化切换到tasks中的其他任务
print(4)
tasks = [
asyncio.ensure_future( func1()),
asyncio.ensure_future( func2())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
2.4 async、await关键字 (py3.5)
import asyncio
async def func1():
print(1)
await asyncio.sleep(2) # 遇到IO耗时操作,自动化切换到tasks中的其他任务
print(2)
async def func2():
print(3)
await asyncio.sleep(2) # 遇到IO耗时操作,自动化切换到tasks中的其他任务
print(4)
tasks = [
asyncio.ensure_future( func1()),
asyncio.ensure_future( func2())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
三、协程的意义
在一个线程中如果遇到IO等待时间(比如本地磁盘数据读取,及网络数据请求),线程不会干等,利用空闲的时间再去干其他事。
案例:去下载三张图片(网络IO)
普通方式(也就是同步编程):
import requests
def download_image(url):
print("开始下载:", url) #发送网络请求,下载图片
response = requests.get(url)
print("下载完成")
# 图片保存到本地文件
file_name = url.rsplit('_')[-1]
with open(file_name, mode='wb') as file_object:
file_object.write(response.content)
if __name__ == "_main__":
url_list = [
"https://www3.autoimg.cn/newsdfs/g26/M02/35/A9/120x90_0_autohomecar_ChsEe12AX06A0OH_AAFocMs8nzu621.jpg",
"https://www2.autoimg.cn/newsdfs/g30/M01/3C/E2/120x90_0_autohomecar_chcCSV2BBICAUntfAADjJFd6800429.jpg",
"https://www3.autoimg.cn/newsdfs/g26/M0B/3C/65/120x90_0_autohomecar_ChcCP12BFCmAIO83AAGq7vK0sGY193.jpg"
]
for item in url_list:
download_image(item)
协程方案(也就是异步编程):
import aiohttp
import asyncio
async def fetch(session, url):
print("发送请求:", url)
async with session.get(url, verify_ssl=False) as response:
content = await response.content.read()
file_name = url.rsplit('_')[-1]
with open(file_name, mode="wb") as file_object:
file_object.write(content)
async def main():
async with aiohttp.ClientSession() as session:
url_list =["https://ww3.autoimg.cn/newsdfs/g26/M02/35/A9/120x90_0_autohomecar_ChsEe2AX06A00H_AAFOCHs8nzU621.jpg",
"https://ww2.autoimg.cn/newsdfs/g30/M01/3C/E2/120x90_0_autohomecar_ChcCSV288ICAUntfAADjJFd6800429.jpg",
"https://wm3.automg,cn/newsdfs/g26/M0B/3C/65/120x90_0_autohomecar_ChcCP12BFCmA083AAGq7VK0sGY193.jpg"
]
tasks = [asyncio.create_task(fetch(session, url)) for url in url_list]
await asyncio.wait(tasks)
if __name__ == "__main__":
asyncio.run(main())
四、快速上手
4.1 基本语法
协程函数,定义函数时候用async修饰,如下
async def func():
pass
result = func() # 获取协程对象
注意,执行协程函数创建协程对象,函数内部代码不会执行;如果想要运行协程函数内部代码,必须要讲协程对象交给事件循环来处理。如下
import asyncio
async def func():
print("快来搞我吧!")
result = func()
# ss1oop = asyncio.get_event_loop()
# ss1oop.run_until_complete( result )
asyncio.run( result ) # python3.7
4.2 await
示例一:
await + 可等待的对象(协程对象,Future,Task对象)
import asyncio
async def func():
print("快来搞我吧!")
response = await asyncio.sleep(2)
print("结束", response)
result = func()
ss1oop = asyncio.get_event_loop()
ss1oop.run_until_complete( result )
#asyncio.run( result ) # python3.7
4.3 Task对象
在事件中循环中添加多个任务。
Tasks用于并发调度协程,通过asyncio.create_task(协程对象)的方式创建Task对象,这样可以让协程加入事件循环中等待被调度执行。除了使用 asyncio.create_task(函数以外,还可以用低层级的loop.create_task0或 ensure_future()函数。不建议手动实例化 Task 对象。
注意:asyncio.create_task() 函数在 Python 3.7 中被加入。在 Python 3.7 之前,可以改用低层级的asyncio.ensure_future()函数。
示例一:
实例2:
4.4 asyncio.Future对象
Task继承Future,Task对象内部await结果的处理基于Future对象来的。
示例一:
示例二:
4.5 concurrent.futures.Future对象
使用线程池、进程池实现异步操作时用到的对象。