这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战
一、 协程
协程不是计算机提供,是程序员任务创造。
协程(Coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术。简而言之,其实就是通过一个线程实现代码块互相切换执行。例如:
def func1():
print(1)
...
print(2)
def func2():
print(3)
...
print(4)
func1()
func2()
实现协程有这么几种方法:
greenlet,早期模块。
yield关键字。
asyncio装饰器(py3.4)
async、await关键字(py3.5)【推荐】
1、 greenlet实现协程
pip install greenlet
from greenlet import greenlet
def func1():
print(1) # 第二步:输出1
gr2.switch() # 第三步:切换到func2函数
print(2) # 第六步:输出2
gr2.switch() # 第七步:切换到func2函数
def func2():
print(3) # 第四步:输出3
gr1.switch() # 第五步:切换到func1
print(4) # 第八步:输出4
gr1 = greenlet(func1)
gr2 = greenlet(func2)
gr1.switch() # 第一步:去执行func1函数
运行结果如下:
2、 yield关键字
def func1():
yield 1
yield from func2()
yield 2
def func2():
yield 3
yield 4
f1 = func1()
for item in f1:
print(item)
运行结果如下:
3、 asyncio
在Python3.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))
遇到IO阻塞自动切换。
运行结果如下:
4、 async & await关键字
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)。
1、 普通方式
import requests
def download_img(url):
print("开始下载:{}".format(url))
response = requests.get(url)
print("下载完成")
file_name = url.split('/')[-1]
with open(file_name, 'wb') as f:
f.write(response.content)
if __name__ == "__main__":
url_list = [
'https://img.tupianzj.com/uploads/allimg/202007/9999/82a6ad0de7.jpg',
'https://img.tupianzj.com/uploads/allimg/180404/9-1P404113S5.jpg',
'https://img.tupianzj.com/uploads/allimg/202007/9999/0aa6ec4f9b.jpg'
]
for item in url_list:
download_img(item)
运行结果如下:
2、 协程方式
'''
下载图片使用第三方模块aiohttp,需要提前安装:pip install aiohttp
'''
import asyncio
import aiohttp
async def fetch(session, url):
print("发送请求:{}".format(url))
async with session.get(url, verify_ssl=False) as response:
content = await response.content.read()
file_name = url.split('/')[-1]
with open(file_name, mode='wb') as f:
f.write(content)
print("下载完成")
async def main():
async with aiohttp.ClientSession() as session:
url_list = [
'https://img.tupianzj.com/uploads/allimg/191126/29-191126102621.jpg',
'https://img.tupianzj.com/uploads/allimg/201910/9999/7801dde033.jpg',
'https://img.tupianzj.com/uploads/allimg/200624/30-200624103209.jpg'
]
tasks = [asyncio.create_task(fetch(session, url)) for url in url_list]
await asyncio.wait(tasks)
if __name__ == "__main__":
asyncio.run(main())
运行结果如下: