在笔记1中追踪了 call_soon这中调用方式是如何运行的,这次看一看 run_until_complete是怎么工作的.

还是由一个简单的例子开始:

import asyncio

async def hi(loop):

print("Hello World")

loop = asyncio.get_event_loop()

# Blocking call which returns when the display_date() coroutine is done

loop.run_until_complete(hi(loop))

loop.stop()

loop.close()

一开始还是通过asyncio.get_event_loop()得到事件循环, 之后调用了 run_until_complete,而调用的参数是一个 async 修饰过的函数的返回值,

async def hi():

print("Hello World")

a = hi()

print(type(a))

#Out[3]: coroutine

可以通过简单的调用 type 来得到它的返回类型, 至于到底是什么, 可参见 asyncio.coroutine , 如果非要追究到底,其实在cpython的源码中是有写的,

typedef struct {

/* The gi_ prefix is intended to remind of generator-iterator. */

_PyGenObject_HEAD(gi)

} PyGenObject;

typedef struct {

_PyGenObject_HEAD(cr)

} PyCoroObject;

可以看出 coroutine和 生成器 其实是一个根源.因此通过 dir() 得到的结果也是很相似的.

回到 run_until_complete, 我们将一个 coroutine 传给它,之后发生了什么?

def run_until_complete(self, future):

self._check_closed()

future = tasks.ensure_future(future, loop=self)

future.add_done_callback(_run_until_complete_cb)

try:

self.run_forever()

except:

...

future.remove_done_callback(_run_until_complete_cb)

if not future.done():

raise RuntimeError('Event loop stopped before Future completed.')

return future.result()

去点注释和非关键部分就如上所示.可以看到传进去的 coroutine 又传给了 tasks.ensure_future, 继续追踪,发现其实这个 coroutine传到了 tasks.Task中用来构造 Task 对象了.而在构造 Task 对象之后或者准确的说的是构造的末尾, 通过 self._loop.call_soon(self._step)调用了 Task 对象的 _step 方法. 在其中,通过

result = coro.send(None)

开始执行我们传递进去的 coroutine, 由于出自生成器, 所以 send 的调用作用是一致的, 都是执行到 挂起 的位置返回. 比如下面简单的示例,

async def hi():

print("Hello World")

a = hi()

try:

result = a.send(None)

except StopIteration as exc:

print(exc.value)

#输出结果如下

Hello World

None

同理,这里的 result = coro.send(None) 也是如此, 如果触发了 StopIteration, 说明 coroutine 已经运行完了.如果没有呢, 比如在 coroutine中遇到了 挂起 呢?

blocking = getattr(result, '_asyncio_future_blocking', None)

首先会检查是否阻塞.先说没有阻塞即 blocking is None 的情况:

情况一 inspect.isgenerator(result) result是一个生成器:

这种在新的语法中是不允许的, 及在 coroutine中不允许再使用yield, 而应使用 yield from

情况二 result is None:

这种是​ coroutine返回了一次迭代但是并没有执行完, 这种可以看到是直接通过

self._loop.call_soon(self._step)进行了下一次迭代.

剩余情况 :

错误情况,直接抛出异常

那blocking  非 None的情况呢? 首先要知道什么 coroutine会返回 blocking is not None.

我们可以简单的从字面看出, coroutine 中需要有阻塞才可以. 可以简单的尝试三个情景:

import asyncio

async def no_block_no_return():

print("haha")

async def no_block_with_return():

print("haha")

return 1

async def with_block():

await asyncio.sleep(1.0)

print("haha")

return 1

first = no_block_no_return()

second = no_block_with_return()

third = with_block()

try:

first_result = first.send(None)

except StopIteration as exc:

print(exc.value)

try:

second_result = second.send(None)

except StopIteration as exc:

print(exc.value)

try:

third_result = third.send(None)

except StopIteration as exc:

print(exc.value)

'''

result:

haha

None

haha

1

'''

再检查,发现只有 third_result 是有定义的, 而且是 _asyncio.Future, 这时检查 blocking

assert getattr(third_result, '_asyncio_future_blocking', None)

会发现这就是我们要找的 blocking is not None.也就是在 coroutine中触发另一个coroutine.

这个 _asyncio.Future是c实现的 Future ,可以在 CPython 源码的 Modules/_asynciomodule.c中找到相关的定义, 其实和 asyncio.Future是类似的.所以我们这里参考python实现的 Future.

那么 await到底发生了什么?

其实要理解await到底做了啥也不难.前面已经说过,coroutine 和生成器类似.比如下面的代码:

async def with_block():

await asyncio.sleep(1.0)

print("haha")

return 1

翻译成没有await关键字的python代码就成了:

def fake_with_block():

#asyncio.sleep 示意如下

loop = asyncio.get_event_loop()

future = loop.create_future()

h = future._loop.call_later(

1.0, futures._set_result_unless_cancelled, future,None)

def _await():

while not future.done():

yield future

yield from _await()

#以上就是await的不完全等价的一个示意,注:只适用于此特殊情景,不能套用

print("haha")

return 1

也就是await后面的(同一行)语句(表达式)悄悄的执行了并和生成器似的返回了,不过返回了一个future对象.

所以在blocking is not None 的语块内可以看到:

result.add_done_callback(self._wakeup)

也就是添加了future结束后的操作.那怎么知道 await后面的(同一行)语句(表达式)运行完了呢?于是asyncio.future中有了set_result的方法.通过set_result来表明future执行完了并执行绑定的call_back.而这里的sleep是通过call_later延迟调用的方式执行set_result.

于是所有的操作就都合并到了loop._run_once中了.这样就完整的循环起来了.

可await后面(后面的行)还有语句要执行啊?添加的那个call_back,就是负责await部分完成了后面怎么运行的.

def _wakeup(self, future):

try:

future.result()

except Exception as exc:

self._step(exc)

else:

self._step()

self = None

可以看到,await执行完,如果没有异常,还是继续_step也就是coro.send(None),也就是继续await后面(后面的行)的内容,这样一个coro就能完整的运行了.

至此,future = tasks.ensure_future(future, loop=self)就剖析完了.后面的

future.add_done_callback(_run_until_complete_cb)

try:

self.run_forever()

except:

...

future.remove_done_callback(_run_until_complete_cb)

就是异常处理和开始事件循环,在笔记1中已经提过了就不再赘余.

python loop until_python3-asyncio 学习笔记 3 -- run_until_complete相关推荐

  1. python loop call soon_python3-asyncio 学习笔记 1 -- call_soon

    最近在学习python3 的 asyncio, 特将学习笔记记录于此. 先来个简单的例子: import asyncio def hello_world(loop): print('Hello Wor ...

  2. 36篇博文带你学完opencv :python+opencv进阶版学习笔记目录

    基础版学习笔记传送门 36篇博文带你学完opencv :python3+opencv学习笔记汇总目录(基础版) 进阶版笔记 项目 opencv进阶学习笔记1: 调用摄像头用法大全(打开摄像头,打开摄像 ...

  3. Python中索引的学习笔记

    1 前言 今天在学习FaceBoxes- 看到一个比较奇怪的代码,"order = scores.argsort()[::-1][:args.top_k]",不太懂这个" ...

  4. Python地理做图——学习笔记

    Python地理做图--学习笔记 GMT 绘制海岸线 绘制地形并叠加海岸线 地理信息数据格式在线转换网址 适用OSGEO4w可以实现tif转nc,转grd 绘制grd和nc 除了投影方式-X, gmt ...

  5. 基于python的数字图像处理--学习笔记(三)

    基于python的数字图像处理--学习笔记(三) 前言 一.灰度拉伸 二.幂律(伽马)变换 三.对数变换 前言 进入冈萨雷斯的第三章内容,并用python实现功能.我更改了代码源,之前找到太烂了,代码 ...

  6. Python第三方库pygame学习笔记(一)

    Pygame Python最经典的2D游戏开发第三方库,也支持3D游戏开发 Pygame适合用于游戏逻辑验证.游戏入门及系统演示验证 Pygame是一种游戏开发引擎,基本逻辑具有参考价值 pygame ...

  7. python气象数据可视化学习笔记6——利用python地图库cnmaps绘制地图填色图并白化

    文章目录 1. 效果图 2. cnmaps简介及安装 2.1 写在前面 2.2 cnmaps简介和安装 3. 导入库 4. 定义绘图函数 4.1 使用get_adm_maps返回地图边界 4.2 ax ...

  8. Python快速编程入门#学习笔记01# |第一章 :Python基础知识 (Python发展历程、常见的开发工具、import模块导入)

    全文目录 ==先导知识== 1 认识Python 1.1.1 Python的发展历程 1.1.2 Python语言的特点 2. Python解释器的安装与Python程序运行 1.2.1 安装Pyth ...

  9. Python+cplex运筹优化学习笔记(三)-营养膳食选择

    Python+cplex运筹优化学习笔记(三)-营养膳食选择 前言 首先呢,说明一下,本文只是自己在学习过程中运用到的例子,然后规整总结一下,随便写写自己所做的一些笔记.小白学习,有不对的地方还望大家 ...

  10. 全国计算机等级考试二级Python精品题库学习笔记1

    全国计算机等级考试二级Python精品题库学习笔记1 精品试卷01 精品试卷01程序题 基本操作题 2:随机验证码 基本操作题 3:比赛成绩计算 Turtle 绘图题:同心圆 简单应用题 2:员工工资 ...

最新文章

  1. syslog-ng 正确配置udp接受端口
  2. java数据库初始化参数,oracle数据库初始化参数设置--Java免费学习网
  3. 使用proc文件系统
  4. 索尼 android l,家庭影院级音质 索尼Xperia i1参数全曝光
  5. BZOJ 1101: [POI2007]Zap( 莫比乌斯反演 )
  6. element 项目 示例_Java ArrayDeque element()方法与示例
  7. http响应头中X-Frame-Options的作用及危害
  8. YlmF WinXP SP3精简版安装显卡驱动技巧
  9. Linux驱动(12)--LED驱动
  10. (转)对各种初始化函数的理解:OnInitDialog、InitInstance、InitApplication函数的理解...
  11. MIMO-OTFS in High-Doppler Fading Channels:Signal Detection and Channel Estimation(5)
  12. 《RSSHub Radar》可以帮助你快速发现和订阅当前网站RSS的浏览器扩展
  13. iphone远没有android好用,IPhone真的比Android流畅?不要被视觉欺骗了
  14. 微信小程序:图标的使用(icon)
  15. Ubantu18.04
  16. mysql bigint 长度最大多少位_bigint 有多少位
  17. 【微机原理与接口技术】学习笔记5 I/O接口和并行接口芯片8255A
  18. DBA_DATAPUMP_JOBS
  19. 天池竞赛员工离职预测训练赛
  20. 劫持PR值的几个方法 假PR值成真PR值

热门文章

  1. 我的世界java1如何安装mod_《我的世界》【教程】如何安装MOD【PC】
  2. gm220s路由器怎么设置_巴法诺无线路由器连接打印机怎么设置【图文教程】
  3. 最喜欢计算机和科学英语作文,英语作文:我最喜欢的科学家
  4. java模拟时间_使用生成器模拟时间分片
  5. tt公路车Java配置怎么样_普通公路车换TT车把可以吗,别的东西还有需要换的吗?...
  6. mysql bean分页查询_javabean 来实现 MySQL 的分页
  7. linux接收网络数据并存存储,Linux网络设备驱动之数据接收流程(六)
  8. linux内核启用64位除法,关于内核中的乘法和除法。
  9. flutter 刷脸_支付宝刷脸认证 - osc_bkdv2it5的个人空间 - OSCHINA - 中文开源技术交流社区...
  10. 【AMAD】django-activity-stream