python多协程
多协程运行原理
单线程的执行路径
多协程的执行路径
协程的运行核心原理是
1、程序在线程里创建一个超级循环(while True)
2、将每一个任务放到任务列表中
3、运行这个超级循环,当遇到CPU操作时,CPU执行;当遇到IO操作时,此时CPU切换到下一个任务的CPU操作,此时IO等待过程中CPU继续运行,不会造成资源浪费
4、一直运行这个循环,直至所有任务执行完毕。
协程的发展
python2.x对协程的支持比较有限
- 生成器yield实现了一部分但不完全;
- greenlet库可以实现协程但是需要手动切换;
- gevent模块倒是有比较好的实现;
python3.4加入了asyncio模块;
python3.5又提供了async/await语法层面的支持;
Python3.6中asyncio模块更加完善和稳定。
python多协程(异步IO)
注:本文代码使用python版本为python3.7+
首先介绍几个概念:
- 1、event_loop 事件循环:超级循环,可以把一些函数注册到这个事件循环上,当满足条件时,就会调用对应的处理方法。
- 2、coroutine 协程:协程对象,只一个使用async关键字定义的函数,他的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环中,由事件循环调用。
- 3、task 任务:一个协程对象就是一个原生可以挂起的函数,任务则是对协程的进一步封装,其中包含任务的各种状态。
- 4、future:代表将来执行或没有执行的任务结果。它与task没有本质的区别。
- 5、async/await 关键字:python3.5用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。
import asyncio
# 获取时间循环
loop = asyncio.get_event_loop()
# 定义协程函数
async def myfunc(url):
await get_url(url)
# 创建task列表
tasks = [loop.create_task(myfunc(url)) for url in urls]
def callback(future):
print future.result()
for task in tasks:
task.add_done_callback(callback)
"""
绑定回调:在task执行完毕的时候可以获取执行的结果,回调的最后一个参数是future对象,通过这个对象可以获取协程的返回值,如果回调函数需要多个参数,可以通过偏函数导入,即task.add_done_callback(functools.partial(call_back_2, 100))
"""
# 执行事件列表
loop.run_until_complete(asyncio.wait(tasks))
"""
run_until_complete根据传递的参数的不同,返回的结果也有所不同
1、run_until_complete()传递的是一个协程对象或task对象,则返回他们finished的返回结果(前提是他们得有return的结果,否则返回None)
2、run_until_complete(asyncio.wait(多个协程对象或任务)),函数会返回一个元组包括(done, pending),通过访问done里的task对象,获取返回值
3、run_until_complete(asyncio.gather(多个协程对象或任务)),函数会返回一个列表,列表里面包括各个任务的返回结果,按顺序排列
"""
案例:
import asyncio
import aiohttp
import time
# 协程函数
async def async_crawl(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
result = await resp.text()
print(f"craw url: {url}, {len(result)}")
return result
# 回调函数
def callback(future):
print(future.result())
if __name__ == '__main__':
loop = asyncio.get_event_loop()
tasks = [
loop.create_task(async_crawl(url))
for url in ["http://www.baidu.com", "http://www.baidu.com"]
]
for task in tasks:
task.add_done_callback(callback)
start =time.time()
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print(end - start)