事件循环
理解成一个死循环,去检测并执行某些代码
协程函数和协程对象
比如:async def func():
这样定义的一个函数就是协程函数
执行协程函数得到的对象叫做协程对象:result = func()
,注意这样执行只会生成对象,而不会执行func()函数的内部代码
那么怎样才能执行它的内部代码呢,这就需要把协程对象交给事件循环去执行:
import asyncio
async def func():
print(1)
result = func()
loop = asyncio.get_event_loop()
loop.run_until_complete(result)
在python3.7之后提供了一个更加简单的事件循环的代码:
import asyncio
async def func():
print(1)
result = func()
asyncio.run(result) # 代替了原来的两行loop代码
await关键字
await+可等待的对象(协程对象、Future对象、Task对象->IO等待)
示例:
import asyncio
async def others():
print('start')
await asyncio.sleep(2)
print('end')
return '返回值'
async def func():
print('执行协程函数内部代码:')
response = await others()
print('IO请求结束,结果为:', response)
asyncio.run(func())
task对象
task对象的作用:立即将一个任务放入事件循环
示例:
import asyncio
async def func():
print(1)
await asyncio.sleep(2)
print(2)
return 'return value'
async def main():
print('start main')
task1 = asyncio.create_task(func())
task2 = asyncio.create_task(func())
print('end main')
ret1 = await task1
ret2 = await task2
print(ret1, ret2)
asyncio.run(main())
对于上面这段代码,首先我们执行main函数,打印start main,然后添加了两个task对象,这两个task对象都将func任务加入事件循环,但是并没有执行。打印完end main之后,遇到了await关键字,await是等待相应的协程执行完并获取结果。task1在执行时遇到了io操作阻塞两秒,自动切换到事件循环中的task2执行。最后的返回结果是:
上面的代码我们做一下简化:
import asyncio
async def func():
print(1)
await asyncio.sleep(2)
print(2)
return 'return value'
async def main():
print('start main')
task_list = [
asyncio.create_task(func()),
asyncio.create_task(func())
]
print('end main')
done, pending = await asyncio.wait(task_list, timeout=None)
print(done)
asyncio.run(main())
或者:
import asyncio
async def func():
print(1)
await asyncio.sleep(2)
print(2)
return 'return value'
task_list = [
func(),
func()
]
done, pending = asyncio.run(asyncio.wait(task_list))
print(done)
task对象和future对象都可以用来查看事件的执行状态(pending和finished),不过task需要依赖loop创建,future则不需要
uvloop:提高asyncio事件循环效率
绑定回调
学完之后,我有一个疑问,就是await在什么时候使用?我是这样理解的,要在所有发生IO或者网络IO等耗时操作的代码前面加上await表示线程挂起并执行事件循环中的其他任务。先就按这个理解写在这里,之后如果发现不对再回来改。
asyncio的正确使用姿势
我们来整理一下asyncio实现并发的两种错误姿势和六种正确姿势:
import asyncio
import time
start = time.time()
async def f1():
print(1)
await asyncio.sleep(2)
print(2)
async def f2():
print(3)
await asyncio.sleep(1)
print(4)
# 第一种错误用法
# async def main():
# await f1()
# await f2()
# 第一种正确用法
# async def main():
# await asyncio.gather(f1(), f2())
# 第二种正确用法
# async def main():
# await asyncio.wait([f1(), f2()])
# 第三种正确用法
# async def main():
# task1 = asyncio.create_task(f1())
# task2 = asyncio.create_task(f2())
# await task1
# await task2
# 第四种正确用法,记住await task要写在后面,因为task相当于立即在事件循环中加入任务
# async def main():
# task1 = asyncio.create_task(f2())
# await f1()
# await task1
# 第二种错误用法, 不能这样简写
# async def main():
# await asyncio.create_task(f1())
# await asyncio.create_task(f2())
# 第五种正确用法
# async def main():
# task1 = asyncio.ensure_future(f1())
# await f2()
# await task1
# 第六种正确用法
async def main():
loop = asyncio.get_event_loop()
task1 = loop.create_task(f1())
await f2()
await task1
if __name__ == '__main__':
asyncio.run(main())
print('time:', time.time()-start)