Introduction

在上一部分我们学习了一种新的用生成器来组织一系列异步callbacks 的方法.加上deferred,我们已经有两种组织异步操作的方法了.

有时候,我们想让一组异步操作并行的运行.因为twisted 是单线程的,它不会真正的并行的运行,但是我们想要异步的I/O 在一组任务上运行的尽可能的快.比如我们的poetry client,从多个server上同时下载诗,而不是一个接一个的.这就是我们为什么用twisted.

我们的opetry client 不得不解决这个问题:你怎么知道你所有的异步操作什么时候能结束?目前为止我们是用把所有的返回结果放进一个list里面,并检查这个list 的长度.我们必须在收集结果的时候非常注意,因为一个错误的结果可能让我们的程序永久的运行下去.

就如你想象的那样,twisted 包含了一个解决这个问题的抽象,我们今天就会学习一下它的用法.

The DeferredList

DeferredList类允许我们把一个deferred 对象的列表当成一个deferred来对待.这样的话我们就可以开启多个异步的操作并在它们全部执行完的时候得到通知.让我们看一些例子.
在deferred-list/deferred-list-1.py,你会发现这些代码:

from twisted.internet import defer

def got_results(res):
    print 'We got:', res

print 'Empty List.'
d = defer.DeferredList([])
print 'Adding Callback.'
d.addCallback(got_results)

如果你运行它,你会得到如下的输出:

Empty List.
Adding Callback.
We got: []

需要注意的一些事情:

DeferredList 是从python 的list 创建而来.在这种情况下这个list 是空的,但是我们会看到这个list 里面的对象必须都是Deferred 对象
    DeferredList 也是一个deferred 对象,它继承至Deferred.这就意味着你可以向它加入callback 和errback,就像它是一个普通的deferred一样
    在上面的例子中,我们的callback在被我们加入之后立即触发,所以这个DeferredList一定已经立马触发了.我们过一会会继续讨论这个
    deferred list 的返回结果是一个空的list

现在让我们看一下 deferred-list/deferred-list-2.py:

from twisted.internet import defer

def got_results(res):
    print 'We got:', res

print 'One Deferred.'
d1 = defer.Deferred()
d = defer.DeferredList([d1])
print 'Adding Callback.'
d.addCallback(got_results)
print 'Firing d1.'
d1.callback('d1 result')

现在我们创建了一个包含一个deferred 对象的DeferredList,下面是我们得到的信息:

One Deferred.
Adding Callback.
Firing d1.
We got: [(True, 'd1 result')]

一些注意的事情:

这一次DeferredList没有触发它的callback直到我们触发了list中的deferred
    这个结果仍旧是一个list,不过这一次有了一个元素
    这个元素是一个tuple,它的第二个值是它对应的deferred 的结果

让我们向list中添加两个deferred,在deferred-list/deferred-list-3.py:

from twisted.internet import defer

def got_results(res):
    print 'We got:', res

print 'Two Deferreds.'
d1 = defer.Deferred()
d2 = defer.Deferred()
d = defer.DeferredList([d1, d2])
print 'Adding Callback.'
d.addCallback(got_results)
print 'Firing d1.'
d1.callback('d1 result')
print 'Firing d2.'
d2.callback('d2 result')

下面是输出:

Two Deferreds.
Adding Callback.
Firing d1.
Firing d2.
We got: [(True, 'd1 result'), (True, 'd2 result')]

DeferredList 的结果是一个数量和DeferredList 中deferred的数量的相同的list.结果的中的每一个元素包含了和它相对应的deferred 的返回结果,前提是这个deferred运行成功.这就意味着DeferredList不会触发直到list 中的所有中的deferred 都已经触发.一个包含空列表的DeferredList 会立即触发.
DeferredList 中deferred 运行的顺序是怎样的呢? 看一下 deferred-list/deferred-list-4.py:

from twisted.internet import defer

def got_results(res):
    print 'We got:', res

print 'Two Deferreds.'
d1 = defer.Deferred()
d2 = defer.Deferred()
d = defer.DeferredList([d1, d2])
print 'Adding Callback.'
d.addCallback(got_results)
print 'Firing d2.'
d2.callback('d2 result')
print 'Firing d1.'
d1.callback('d1 result')

现在我们先触发d2然后触发d1.下面是输出:

Two Deferreds.
Adding Callback.
Firing d2.
Firing d1.
We got: [(True, 'd1 result'), (True, 'd2 result')]

输出列表有着和原来的list 的一样的顺序,而不是被触发的顺序.这样非常好,因为我们可以很好的把输出结果和deferred很好的关联起来.

好的,如果DeferredList 中的deferred 有一个失败了会发生什么?输出中的那些True 是做什么用的?让我们看 deferred-list/deferred-list-5.py:

from twisted.internet import defer

def got_results(res):
    print 'We got:', res

d1 = defer.Deferred()
d2 = defer.Deferred()
d = defer.DeferredList([d1, d2], consumeErrors=True)
d.addCallback(got_results)
print 'Firing d1.'
d1.callback('d1 result')
print 'Firing d2 with errback.'
d2.errback(Exception('d2 failure'))

现在我们用一个正常的结果触发d1,用一个error来触发d2.咱们先暂时忽略掉 consumeErrors 选项,下面是输出:

Firing d1.
Firing d2 with errback.
We got: [(True, 'd1 result'), (False, >)]

现在和d2 对应的返回结果出现一个错误.到现在我们应该清楚DeferredList 是怎样工作的:

DeferredList是被一个deferred 的列表组成的
    DeferredList本身也是一个deferred,它的返回结果是一个长度和DeferredList本身长度的列表
    DeferredList在列表中所有deferred都触发之后才被触发
    返回结果列表中的每一个元素对应着DeferredList中的每一个deferred.加入那个deferred成功了 这个元素是(True,result),假如这个deferred失败了,这个元素是(False,failure)
    一个DeferredList不会失败,因为每一个deferred 无论成功与否它的结果都会被搜集到返回的列表中

下面让我们看一下consumeErrors 选项,假如我们不设置consumeErrors选项(deferred-list/deferred-list-6.py),我们会得到如下的输出:

Firing d1.
Firing d2 with errback.
We got: [(True, 'd1 result'), (False, >twisted.python.failure.Failure >type 'exceptions.Exception'<<)]
Unhandled error in Deferred:
Traceback (most recent call last):
Failure: exceptions.Exception: d2 failure

如果你回想一下,在deferred 中未处理的错误信息会在deferred 被垃圾回收的时候被抛出来.这个信息告诉我们我们没有全部的捕捉我们异步程序中的错误.这个错误信息从哪里来的呢?它明显的不是从DeferredList来的,所以这个错误一定来自d2.

DeferredList需要知道它下面的deferreds都在什么时候触发.DeferredList 通过增加一个callback和errback到每一个deferred,这样就可以监测了.默认的,这个callback(errback) 返回正常的结果(错误),因为返回错误会触发下一个errback,这样d2 在触发之后会保持失败状态.
但是假如我们设置consumeErrors 为True,向每一个deferred加入的errback 会返回None.我们也可以向d2加入自己的errback,例子在deferred-list/deferred-list-7.py.

Client 8.0

我们的poetry client 的8.0版本使用DeferredList去监测什么时候所有的poetry全部下载完.你可以在twisted-client-8/get-poetry.py看到代码.唯一的变化是poetry_main.让我们看一下主要的变化:

...
ds = []

for (host, port) in addresses:
    d = get_transformed_poem(host, port)
    d.addCallbacks(got_poem)
    ds.append(d)

dlist = defer.DeferredList(ds, consumeErrors=True)
dlist.addCallback(lambda res : reactor.stop())

在client 8.0 中,我们不需要poem_done callback 或者 results list.相反的,我们把从get_transformed_poem 获得的deferred 全部放入一个list,并创建一个DeferredList.因为DeferredList 会直到所有的deferred触发之后才会被触发.我们向DeferredList增加一个callback来关闭reactor.在这种情况下,我们没有使用DeferredList的返回结果,我们只需要知道什么时候所有的事情能结束.

Discussion
我们可以用图片形象话一个DeferredList 是怎样工作的,图片三十七:


图片三十七

真的很简单,仍旧有几个DeferredList 的参数我们没有覆盖掉.这些参数会改变DeferredList的默认行为,感兴趣的可以自己看.

在下一部分我们还会讲deferred 的一个特色,一个刚被twisted 10.1.0 加进去的.

twisted系列教程十八–异步操作的并行运行相关推荐

  1. ComicEnhancerPro 系列教程十八:JPG文件长度与质量

    作者:马健 邮箱:stronghorse_mj@hotmail.com 主页:http://www.comicer.com/stronghorse/ 发布:2017.07.23 教程十八:JPG文件长 ...

  2. twisted系列教程十九–cancel deferred

    Introduction twisted 是一个正在发展的项目,twisted 的开发者们会添加一些新的特色或者扩展旧的.随着twisted 10.1.0 的发布,开发者们增加了一个新的功能–取消,这 ...

  3. twisted系列教程十四— pre-fireed deferred

    Introduction 在这一部分我们将要学习deferred 类的另外的一个方面.为了促进讨论,我们要为我们的poetry service增加一个server.假设我们有大量的内部的client ...

  4. twisted系列教程十五–测试twisted代码

    Introduction 在这个系列中我们也已经写了很多twisted 代码了,但目前为止我们忽略了一个很重要的事情-测试.你可能也一直在想我们怎样用一个同步的测试框架unitest来测试我们的异步的 ...

  5. twisted系列教程十六–twisted守护进程

    Introduction 到目前为止我们写的server 还运行在一个终端里面,通过print 语句向外输出内容.开发的时候这样做是很有好处的,但是当你部署一个产品的时候这样就不好了.一个生产环境中的 ...

  6. twisted系列教程十二–为server 增加一个service

    One More Server 在第九部分和第十部分我们介绍了关于诗歌的变形引擎的想法,最后我们实现了cummingsifier,我们还让它抛出随机的异常来模拟错误.但是假如这个变形的引擎在另外一台服 ...

  7. twisted系列教程十–可以变化的诗

    Client 5.0 现在我们将要想我们的client中加入一些变形逻辑.但是首先我不得不说:我不知道怎样写一个Byronification 引擎,它超出我的能力范围了.做为替代,我会实现一个相对简单 ...

  8. Vue 教程(十八)template 标签

    Vue 教程(十八)template 标签 template标签 标签必须加 id 使用模板时,需要的中横线 代码实现 <!DOCTYPE html> <html lang=&quo ...

  9. 【Visual C++】游戏开发五十 浅墨DirectX教程十八 雪花飞扬:实现唯美的粒子系统...

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接:http://blog.csdn.net/zhmxy555/article/details/8744805 作者:毛星云(浅墨) ...

最新文章

  1. Error:No suitable device found: no device found for connection “System eth1″
  2. mysql 创建用户远程连接
  3. HTML中放置CSS的三种方式和CSS选择器
  4. 【广告技术】用张量分解预测广告库存,广告投放更可靠!
  5. Atitit 项目成本之道 attilax著 1.1. 循环使用 效果明显 降低50%费用 1 1.2. Diy战略(效果显著)缩短供应链 自己组合使用,效率更高 2 1.3. 扎堆战略 使用广泛
  6. 甄零一诺合同——专注合同信息化管理
  7. ITSM正向浪涌测试仪
  8. 元宇宙虚拟人迎来高峰期,哪个是你的最爱?
  9. python中的sep函数_python sep函数是什么?怎么用?
  10. IL汇编语言介绍(译)
  11. 【生物信息学】正常和突变的蛋白质结构可视化?构建蛋白结构模型,常见的4种构建蛋白模型方法
  12. select语句(3)--单值函数
  13. STM32之数据采集和心率检测仪(原理图、PCB、程序源码等)超详细!!!
  14. 为什么你学不会递归?告别递归,谈谈我的经验
  15. Python爬虫:彼岸图网图片爬取
  16. 愿你遍历山河,觉得人间值得。
  17. SCUT - 249 - A piece of Cake - 组合数学
  18. 贪吃蛇java设计代码下载_java贪吃蛇游戏源代码
  19. 浩辰建筑CAD软件新手入门教程:在位编辑
  20. 持续集成-SVN版本管理系统的安装

热门文章

  1. 被引10万次:21世纪高被引论文Top 10
  2. 一场大病引起的诺贝尔2017年生理学奖角逐
  3. 白鹭引擎增加点击事件实例
  4. oracle 中此处列不允许,oracle-序列 ora-02287 此处不允许序号
  5. informatica数据脱敏_助您首个大数据项目破茧成蝶的实践指南
  6. linux 服务器(CentOS7)搭建PHP环境+SSH配置+服务器文件上传配置
  7. Android笔记-Xposed的使用(Hook登录函数获取用户名密码)
  8. Wireshark笔记-ping,arp相关的实验(2台主机是否能通)
  9. Java Socket笔记-利用tcp socket搭建CS模型
  10. Qt文档阅读笔记-Widgets Tutorial官方解析及实例