在笔记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. 以太坊区块链同步_以太坊69:如何在10分钟内建立完全同步的区块链节点
  2. 云端TensorFlow读取数据IO的高效方式
  3. golang 开源代理
  4. cad卸载_怎么把CAD卸载干净,老司机来教你
  5. 深入解读:获Forrester大数据能力高评价的阿里云DataWorks思路与能力
  6. 招聘数下降71%!程序员:你的努力正在毁掉自己!
  7. 北京大学计算机学院周磊,马秀莉-北京大学信息科学技术学院
  8. 数据预处理和数据特征工程
  9. python所有第三方库_自动更新Python所有第三方库
  10. Windows开始菜单快捷方式位置(磁贴)
  11. 智能陈桥五笔7.8试用编号是多少_如何设计和编写软件测试用例
  12. Java IO——字节流和字符流详解区别对比
  13. android mov转mp4格式转换,如何在线将MOV文件转换成MP4文件?
  14. oracle导入excel乱码,Oracle导出的文件为什么用Excel打开是乱码?
  15. 分享:淘宝客完全开源程序。
  16. 高颜值挂脖式运动蓝牙耳机盘点,五款3D环绕高音质蓝牙耳机测评分享
  17. ElementUI全局配置message的弹窗时间
  18. 【毕业设计】基于STM32及OpenMV的云台追踪装置
  19. 【渝粤教育】21秋期末考试马克思主义基本原理概论(A)10882k1
  20. 甲骨文大数据利器:内存数据库和一体机

热门文章

  1. 12v小型电机型号大全_电机型号参数大全,再不怕看不懂型号了
  2. linux shell调用c 程序设计,linux – 在bash -c中设置变量
  3. Intouch使用SIDirect OIServer 连接西门子PLC S7-300
  4. Pytorch使用Tensorboard记录loss曲线 (Tensorboard学习二)
  5. python转str类型的列表为list格式
  6. django新建utils文件夹与导入方法
  7. java exec source报错_Mac 下maven路径报错的坑
  8. idea设置关键字颜色_IDEA字体颜色快速导入辅助工具设置
  9. linux配置php mysql_Linux下LAMP(Apache+PHP+MySql)环境配置
  10. linux内存管理机制