Twisted学习(二)--------------构造Deferreds

来源:互联网 发布:海龟交易系统源码 编辑:程序博客网 时间:2024/05/29 12:54

构造Deferreds

Deferred对象是一种信号,当你调用一个函数而你想得到的数据还未返回时可以使用它。当一个函数返回了一个deferred对象,你可以为其关联一个函数用于处理数据返回。

这篇教程关注问题的另一半:如果写返回deferred对象的函数,也就是如何构造deferred对象,包括如何直接返回deferred对象而不阻塞代码执行,以及如何在数据可用时调用callbacks。

另外,这篇文档假设你对Twisted的异步编程模型比较熟悉,同时已经阅读过使用函数返回的deferred对象.


类概览

这是一个构造deferred对象和调用其callback或者errback的API参考。主并不意味着它是deferred类API的一个替代,只是提供一个如何使用的指引。

另外一篇关于如何使用函数返回deferred对象的文档,请参考使用函数返回的deferred对象.


基本的callback函数

  • callback(result)

    用指定的结果运行callbacks. 只能调用一次. 多余的调用callback或者errback会导致twisted.internet.defer.AlreadyCalledError .如果在调用这这之后添加了callbacks或者errbacks,那么 addCallbacks 将会直接调用它们.

  • errback(failure)

    用指定的failure运行errbacks. 只能调用一次. 多余的调用errback或者callback会导致twisted.internet.defer.AlreadyCalledError .如果在调用这这之后添加了callbacks或者errbacks,那么 addCallbacks 将会直接调用它们.


什么是Deferred不会做的:把你的代码转换为异步的

Deferred不会像变魔术似的把你的代码变成异步的,我们看下面的例子:

from twisted.internet import deferTARGET = 10000def largeFibonnaciNumber():    # create a Deferred object to return:    d = defer.Deferred()    # calculate the ten thousandth Fibonnaci number    first = 0    second = 1    for i in xrange(TARGET - 1):        new = first + second        first = second        second = new        if i % 100 == 0:            print("Progress: calculating the %dth Fibonnaci number" % i)    # give the Deferred the answer to pass to the callbacks:    d.callback(second)    # return the Deferred with the answer:    return dimport timetimeBefore = time.time()# call the function and get our Deferredd = largeFibonnaciNumber()timeAfter = time.time()print("Total time taken for largeFibonnaciNumber call: %0.3f seconds" % \      (timeAfter - timeBefore))# add a callback to it to print the numberdef printNumber(number):    print("The %dth Fibonacci number is %d" % (TARGET, number))print("Adding the callback now.")d.addCallback(printNumber)

你会注意到尽管在largeFibonnaciNumber函数中创建了deferred对象,发生了下面的事情:

  • "Total time taken for largeFibonnaciNumber call"输出意味着函数并没有直接返回并以我们期望的异步方式来运行。
  • callback并没有在结果返回前添加到deferred对象中,也没有在结果返回时调用,甚至在计算完成之后它也没有添加到deferred对象中。

函数在返回之前已经完成了计算,计算阻塞了当前进程直到完成,这是异步代码不会做的。Deferred不是非阻塞代码的法宝:它们是异步函数用来传递结果给callbacks的信号,但是使用Deferred不保证你得到一个异步函数。


高级进程链控制

  • pause()

    停止调用添加进来的方法,然后不响应callback,直接到调用unpase()calling any methods as they are added, and do notrespond tocallback , untilself.unpause() is called.

  • unpause()

    如果这个deferred对象上的callback已经在运行,则会调用所有添加到此Deferred对象上的callbacks尽管已经调用了pause。

     这会将Deferred 置于一个状态,在这个状态下oaddCallbacks 或者callback 将会正常工作.

从同步函数中返回Deferreds

有时候你希望在同步函数中返回一个deferred.有以下几个原因:

  • 需要与其它返回deferred对象版本的API兼容
  • 将来你的代码可能变为异步的

使用函数返回的deferred对象中,我们给出了以下同步代码:

def synchronousIsValidUser(user):    '''    Return true if user is a valid user, false otherwise    '''    return user in ["Alice", "Angus", "Agnes"]

尽管我们可以要求调用我们函数的代码用maybeDeferred包装同步结果,但是出于API兼容考虑,我们最好自己使用defer.succeed来返回一个deferred对象。

from twisted.internet import deferdef immediateIsValidUser(user):    '''    Returns a Deferred resulting in true if user is a valid user, false    otherwise    '''    result = user in ["Alice", "Angus", "Agnes"]    # return a Deferred object already called back with the value of result    return defer.succeed(result)

有一个等价的defer.fail方法来返回一个errback链已经开始执行的deferred.


用Twisted来集成阻塞代码

在一些情况下,你可能需要调用阻塞代码: 许多第三方库的函数都有需要长时间运行的需要阻塞函数.没有办法强制一个函数变为异步的: 它必须用特定的方式来编写. 当使用Twisted时,你自己的代码需要是异步的, 但是对于第三库,你没有办法重写它们来保证异步.

对于这种情况,Twisted提供了在独立线程中运行阻塞代码而不阻塞我们程序的方法.twisted.internet.threads.deferToThread 将会使用一个线程来运行我们的阻塞代码,返回一个deferred对象并在线程完成时执行callback.

现在我们假设largeFibonnaciNumber 函数是第三方的一个库函数 (返回计算结果而不是deferred对象)要修改它,使它在不同一进程中运行完成而不阻塞是不容易的. 下面的例子展示了在一个线程中运行它, 和前面提到这个程序时不同的是它不会阻塞我们的程序:


def largeFibonnaciNumber():    """    Represent a long running blocking function by calculating    the TARGETth Fibonnaci number    """    TARGET = 10000    first = 0    second = 1    for i in xrange(TARGET - 1):        new = first + second        first = second        second = new    return secondfrom twisted.internet import threads, reactordef fibonacciCallback(result):    """    Callback which manages the largeFibonnaciNumber result by    printing it out    """    print("largeFibonnaciNumber result =", result)    # make sure the reactor stops after the callback chain finishes,    # just so that this example terminates    reactor.stop()def run():    """    Run a series of operations, deferring the largeFibonnaciNumber    operation to a thread and performing some other operations after    adding the callback    """    # get our Deferred which will be called with the largeFibonnaciNumber result    d = threads.deferToThread(largeFibonnaciNumber)    # add our callback to print it out    d.addCallback(fibonacciCallback)    print("1st line after the addition of the callback")    print("2nd line after the addition of the callback")if __name__ == '__main__':    run()    reactor.run()

可能的代码错误

通过提供标准注册callbacks的方式,Deferreds大大简化了编写异步代码的过程。但是使用它们,你可能需要遵循一些微妙甚至困惑的规则。这更多时候是对那些在新系统内部构造并编写Deferreds的人们,而不是那些使用其它系统返回的deferreds对象并在上面添加callbacks的人们。然而,你最好知道这些。


不可能多次执行deferred

Deferred只能执行一次,你只能调用callback或者errback一次。当你添加新的callbacks时,进程链会继续在已经执行的deferred上运行。


同步执行callback

如果一个deferred对象已经有了结果,addcallback可能会直接同步调用callback:也就是说,在它添加之后直接调用。在callback需要修改状态的场合,你可能希望执行链挂起直到所有的callbacks都添加完成。这时你可以使用pause和unpause来控制执行链。

当你使用这些方法时要小心,如果你pause了deferred,你有责任unpause它。添加callbacks的函数必须unpause已经pause的deferred,这不应该是实际上调用callback或者errback执行deferred的代码的责任,因为这会使它们没有作用。




0 0
原创粉丝点击