关于python:python协程asyncio的个人理解

27次阅读

共计 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 秒;

其中的起因须要了解协程的工作机制(事件循环和控制权)

以上就是全部内容,心愿对大家有所帮忙学习,

正文完
 0