Python协程
协程
协程也称为微线程,就是通过一个线程实现代码块相互切换执行。
实现方式
greenlet(早期模块)
yield关键字
asyncio装饰器
async、await关键字【推荐】
1. greenlet实现协程
安装模块
pip install greenlet
示例代码
from greenlet import greenlet def task1(): print(1) # 1. 输出1 gr2.switch() # 切换到gr2 print(2) # 3. 输出2 gr2.switch() # 切换到gr2 def task2(): print(3) # 2. 输出3 gr1.switch() # 切换到gr1 print(4) # 4. 输出4 gr1 = greenlet(task1) gr2 = greenlet(task2) gr1.switch() # 执行task1函数
执行结果
1 3 2 4 进程已结束,退出代码0
2. yield关键字实现协程
注:函数内如果含有yield代表此函数是个生成器函数
示例代码
def task1(): yield 1 yield from task2() # 跳转到task2函数 yield 2 def task2(): yield 3 yield 4 t1 = task1() for item in t1: print(item)
执行结果
1 3 2 4 进程已结束,退出代码0
3. asyncio实现协程
注:在python3.4之后可用
特点:遇到io阻塞自动切换
示例代码
import asyncio @asyncio.coroutine def task1(): print(1) yield from asyncio.sleep(2) # 模拟耗时操作, 自动切换到tasks中的其他任务 print(2) @asyncio.coroutine def task2(): print(3) yield from asyncio.sleep(2) # 模拟耗时操作, 自动切换到tasks中的其他任务 print(4) tasks = [ asyncio.ensure_future(task1()), asyncio.ensure_future(task2()), ] # 生成或获取一个事件循环 loop = asyncio.get_event_loop() # 将任务放到'任务列表' loop.run_until_complete(asyncio.wait(tasks))
执行结果
1 3 2 4 进程已结束,退出代码0
3. async&await关键字实现协程
注:在python3.5之后可用
注:与asyncio的实现区别在于使用async代替了装饰器
示例代码
import asyncio async def task1(): print(1) await asyncio.sleep(2) # 模拟耗时操作, 自动切换到tasks中的其他任务 print(2) async def task2(): print(3) await asyncio.sleep(2) # 模拟耗时操作, 自动切换到tasks中的其他任务 print(4) tasks = [ asyncio.ensure_future(task1()), asyncio.ensure_future(task2()), ] loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(tasks))
执行结果
1 3 2 4 进程已结束,退出代码0
4. 下载图片案例使用协程与不使用协程的对比
不使用协程(同步)
代码
import requests import time def download_img(img_url): print('开始下载:', img_url) resp = requests.get(img_url) print('下载完成:', img_url) file_name = img_url.split('/')[-1] # 提取图片名称 # 保存图片 with open(file_name, 'wb') as f: f.write(resp.content) if __name__ == '__main__': # 要下载的图片url列表 url_list = [ 'http://img2.woyaogexing.com/2017/12/06/6f1480322fb2d1f5!600x600.jpg', 'http://img2.woyaogexing.com/2017/12/06/b9b8d610495a3a9c!600x600.jpg', 'http://img2.woyaogexing.com/2017/12/06/3442cb320452f9d5!600x600.jpg' ] # 统计时间 start_time = time.time() # 遍历列表进行下载 for url in url_list: download_img(url) end_time = time.time() print('总共耗时:{}'.format(round((end_time - start_time) * 1000)) + 'ms')
执行结果
开始下载: http://img2.woyaogexing.com/2017/12/06/6f1480322fb2d1f5!600x600.jpg 下载完成: http://img2.woyaogexing.com/2017/12/06/6f1480322fb2d1f5!600x600.jpg 开始下载: http://img2.woyaogexing.com/2017/12/06/b9b8d610495a3a9c!600x600.jpg 下载完成: http://img2.woyaogexing.com/2017/12/06/b9b8d610495a3a9c!600x600.jpg 开始下载: http://img2.woyaogexing.com/2017/12/06/3442cb320452f9d5!600x600.jpg 下载完成: http://img2.woyaogexing.com/2017/12/06/3442cb320452f9d5!600x600.jpg 总共耗时:179ms
使用协程(异步)
代码
import time import aiohttp import asyncio async def fetch(session, url): print('发送请求: {}'.format(url)) async with session.get(url) as response: content = await response.content.read() file_name = url.split('/')[-1] # 提取图片名称 # 保存图片 with open(file_name, 'wb') as f: f.write(content) print('下载完成: {}'.format(url)) async def main(): async with aiohttp.ClientSession() as session: url_list = [ 'http://img2.woyaogexing.com/2017/12/06/6f1480322fb2d1f5!600x600.jpg', 'http://img2.woyaogexing.com/2017/12/06/b9b8d610495a3a9c!600x600.jpg', 'http://img2.woyaogexing.com/2017/12/06/3442cb320452f9d5!600x600.jpg' ] tasks = [asyncio.create_task(fetch(session, url)) for url in url_list] await asyncio.wait(tasks)
执行结果
发送请求: http://img2.woyaogexing.com/2017/12/06/6f1480322fb2d1f5!600x600.jpg 发送请求: http://img2.woyaogexing.com/2017/12/06/b9b8d610495a3a9c!600x600.jpg 发送请求: http://img2.woyaogexing.com/2017/12/06/3442cb320452f9d5!600x600.jpg 下载完成: http://img2.woyaogexing.com/2017/12/06/6f1480322fb2d1f5!600x600.jpg 下载完成: http://img2.woyaogexing.com/2017/12/06/b9b8d610495a3a9c!600x600.jpg 下载完成: http://img2.woyaogexing.com/2017/12/06/3442cb320452f9d5!600x600.jpg 总共耗时:45ms