共计 4349 个字符,预计需要花费 11 分钟才能阅读完成。
目录
协程与工作
根本语法
协程的申明和运行
可期待对象
运行 asyncio 程序
创立工作
休眠
机制解析
运行的流程图示
协程与工作
python 语境中,协程 coroutine 的概念有两个:协程函数、协程对象,协程对象由协程函数创立失去(相似于类实例化失去一个对象).
了解协程,最重要的是理解事件循环和工作执行的机制,上面是三个准则:
事件循环中,一直循环执行各个工作,若一个工作遇到 await 或者执行实现,则返回控制权给事件循环,这时候事件循环再去执行下一个工作
事件循环同一时刻只会运行一个工作
协程不会被退出事件循环的执行日程,只有被注册为工作之后,事件循环才能够通过工作来设置日程以便并发执行协程
根本语法
协程的申明和运行
应用 async def 语句定义一个协程函数,但这个函数不可间接运行
async def aaa():
print('hello')
print(aaa())
# 输入 ----------------------------------
<coroutine object aaa at 0x7f4f9a9dfec0>
/root/Project/test01/test2.py:4: RuntimeWarning: coroutine 'aaa' was never awaited
print(aaa())
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
如何运行一个协程呢,有三种形式:
1. 应用 asyncio.run()函数,可间接运行
import asyncio
async def aaa():
print('hello')
asyncio.run(aaa())
# 输入 -------------------------
hello
2. 应用 await 进行异步期待
在协程函数中最重要的性能是应用 await 语法期待另一个协程,这将挂起以后协程,直到另一个协程返回后果。
await 的作用:挂起 coroutine 的执行以期待一个 awaitable 对象。只能在 coroutine function 外部应用。
import asyncio
async def aaa():
print('hello')
async def main():
await aaa()
asyncio.run(main())
3. 应用 asyncio.create_task() 函数来创立一个工作,放入事件循环中
import asyncio
async def aaa():
print('hello')
async def main():
asyncio.create_task(aaa())
asyncio.run(main())
可期待对象
下面说过,协程函数中最重要的性能是应用 await 语法期待另一个协程,这将挂起以后协程,直到另一个协程返回后果。(重要,反复一遍)
await 前面须要跟一个可期待对象(awaitable),有上面三种可期待对象:
协程:包含协程函数和协程对象
工作:通过 asyncio.create_task()函数将协程打包为一个工作
Futures:非凡的 低层级 可期待对象,示意一个异步操作的 最终后果
运行 asyncio 程序
asyncio.run(coro, *, debug=False)
传入协程 coroutine coro,创立事件循环,运行协程返回后果,并在完结时敞开,该当被用作 asyncio 程序的主入口点。
创立工作
asyncio.create_task(coro, *, name=None)
将 coro 协程 打包为一个 Task 排入日程筹备执行。返回 Task 对象。
休眠
coroutine asyncio.sleep(delay, result=None, *, loop=None)
阻塞 delay 指定的秒数,该协程总是会挂起当前任务,以容许其余工作运行
机制解析
通过官网的两段代码,来具体解析一下协程的运行机制。
官网两个代码如下,留神看输入差别:
代码 1,通过协程对象来执行
import asyncio
import time
async def say_after(delay, what):
await asyncio.sleep(delay)
print(what)
async def main():
print(f"started at {time.strftime('%X')}")
await say_after(1, 'hello')
await say_after(2, 'world')
print(f"finished at {time.strftime('%X')}")
asyncio.run(main()) # 1:创立事件循环,传入入口点 main()协程对象, 此时生成一个对应的 task
输入为
started at 17:13:52
hello
world
finished at 17:13:55
代码 2,通过工作来执行
import asyncio
import time
async def say_after(delay, what):
await asyncio.sleep(delay)
print(what)
async def main():
task1 = asyncio.create_task(say_after(1, 'hello'))
task2 = asyncio.create_task(say_after(2, 'world'))
print(f"started at {time.strftime('%X')}")
await task1
await task2
print(f"finished at {time.strftime('%X')}")
asyncio.run(main())
输入:
started at 17:14:32
hello
world
finished at 17:14:34
留神到运行工夫比前一个代码快 1 秒,上面阐明为什么呈现这种状况(文字比拟多)。
代码一的运行逻辑:
asyncio.run(main()) 启动一个事件循环,将入口点 main()协程对象传入,生成一个对应的工作 task_main;
事件循环运行工作 task_main,而后执行第 1 条代码:print(f”started at {time.strftime(‘%X’)}”);
接着执行第 2 条代码:await say_after(1, ‘hello’),第 2 条代码首先生成一个 say_after(1, ‘hello’)协程对象,同时生成该协程对象对应的 task_1;
因为 await 语法,task_main 工作将控制权返回给事件循环,同时通知事件循环须要期待 task1 能力持续运行;
事件循环取得控制权后,发现此时有两个工作 task_main 和 task1,同时 task_main 在期待 task1,于是会去执行 task1 工作;
task1 工作将执行第 1 条代码:await asyncio.sleep(1),同样会生成 asyncio.sleep(1)协程对象,以及对应的工作 task2,同时因 await 语法将控制权返回给事件循环;
事件循环取得控制权后,发现此时有三个工作 task_main、task1、task2,因为 task_main、task1 都处于期待状态,于是执行 task3;
task3 在 1 秒后运行实现,返回控制权给事件循环;
事件循环取得控制权,发现此时有两个工作 task_main 和 task1,同时 task_main 在期待 task1,于是会去执行 task1 工作;
task1 工作执行第 2 条代码:print(‘hello’),执行实现后,工作也运行完结,将控制权返回给事件循环;
事件循环取得控制权后,发现此时有一个工作 task_main,于是接着执行下一条代码:await say_after(2, ‘world’),持续反复上述过程,直到这个协程工作完结;
task_main 执行最初一条代码;
事件循环敞开退出;
代码二的运行逻辑:
asyncio.run(main()) 启动一个事件循环,将入口点 main()协程对象传入,生成一个对应的工作 task_main;
事件循环运行工作 task_main,而后执行前几条代码,创立两个工作 task1、task2,并注册到事件循环中(此时事件循环一共有 3 个 task),随之执行程序直到 await;
第一个 await:await task1,这里会阻塞当前任务 task_main 并将控制权返回给事件循环,事件循环获取控制权,安顿执行下一个工作 task1;
task1 工作开始执行,直至遇到 await asyncio.sleep(1),asyncio.sleep(1)协程对象开始异步执行,同时 task1 返回控制权给事件循环,事件循环获取控制权后安顿执行下一个工作 task2;
task2 工作开始执行,直至遇到 await asyncio.sleep(2),asyncio.sleep(2)协程对象开始异步执行,同时 task2 返回控制权给事件循环,事件循环获取控制权后安顿执行下一个工作;
此时 3 个工作均处于 await 状态,事件循环放弃期待;
1 秒后 asyncio.sleep(1)执行实现,task1 勾销阻塞,事件循环将安顿 task1 执行,task1 执行实现后返回控制权给事件循环,此时事件循环中一共两个工作 task_main、task2。
此时 task2 工作处于 await 状态,而 task_main 也勾销了阻塞,事件循环安顿 task_main 执行,执行一行代码后遇到 await task2,于是返回控制权给事件循环;
此时 2 个工作均处于 await 状态,事件循环放弃期待;
1 秒后 asyncio.sleep(2)执行实现,task2 勾销阻塞,事件循环将安顿 task2 执行,task2 执行实现后返回控制权给事件循环,此时事件循环中只剩工作 task_main;
于是事件循环安顿 task_main 执行,task_main 执行实现,asyncio.()函数收到信息也完结运行,整个程序完结
运行的流程图示
(工作就绪后,就期待事件循环来调用了,此时须要 await 来阻塞主工作 task_main,否则控制权始终在 task_main 手上,导致 task_main 工作执行实现,run() 收到 main()执行完结的音讯后,事件循环也敞开并完结,程序也将退出)
其实将第 2 个代码中的 await task1 删除,只保留 await task2,后果中的输入雷同,并耗费雷同的总工夫。但只保留 await task1 的话,将没有 task2 的输入;
如果将第 2 个代码中的 await task1 和 await task2 都删除,换成 await asyncio.sleep(3),一样会打印雷同输入,不过总工夫会变为 3 秒;
其中的起因须要了解协程的工作机制(事件循环和控制权)
以上就是全部内容,心愿对大家有所帮忙学习,