使用twisted和greenlets时出错

时间:2013-10-26 09:11:15

标签: python twisted greenlets

我正在尝试使用twisted with greenlets,因此我可以在不使用inlineCallbacks的情况下编写扭曲的同步代码。

这是我的代码:

import time, functools
from twisted.internet import reactor, threads
from twisted.internet.defer import Deferred
from functools import wraps
import greenlet

def make_async(func):
    @wraps(func)
    def wrapper(*pos, **kwds):
        d = Deferred()

        def greenlet_func():
            try:
                rc = func(*pos, **kwds)
                d.callback(rc)
            except Exception, ex:
                print ex
                d.errback(ex)

        g = greenlet.greenlet(greenlet_func)
        g.switch()

        return d
    return wrapper

def sleep(t):
    print "sleep(): greenelet:", greenlet.getcurrent()
    g = greenlet.getcurrent()
    reactor.callLater(t, g.switch)
    g.parent.switch()

def wait_one(d):
    print "wait_one(): greenelet:", greenlet.getcurrent()
    g = greenlet.getcurrent()
    active = True

    def callback(result):
        if not active:
            g.switch(result)
        else:
            reactor.callLater(0, g.switch, result)

    def errback(failure):
        if not active:
            g.throw(failure)
        else:
            reactor.callLater(0, g.throw, failure)

    d.addCallback(callback)
    d.addErrback(errback)

    active = False
    rc = g.parent.switch()
    return rc

@make_async
def inner():
    print "inner(): greenelet:", greenlet.getcurrent()

    import random, time
    interval = random.random()

    print "Sleeping for %s seconds..." % interval
    sleep(interval)
    print "done"

    return interval

@make_async
def outer():
    print "outer(): greenelet:", greenlet.getcurrent()
    print wait_one(inner())
    print "Here"

reactor.callLater(0, outer)
reactor.run()

共有5个主要部分:

  • 睡眠功能,启动计时器,然后切换回父greenlet。当计时器熄灭时,它会切换回正在休眠的greenlet。
  • 一个make_async装饰器。这需要一些同步的代码并在greenlet中运行它。 IT也会返回延迟,因此调用者可以在代码完成时注册回调。
  • wait_one函数,它阻止greenlet直到等待延迟等待结果。
  • 内部函数(当被包装时)返回延迟,休眠一段随机时间,然后将它睡觉的时间传递给延迟。
  • 调用inner()的外部函数等待它返回,然后打印返回值。

当我运行此代码时,我得到了这个输出(注意最后两行的错误):

outer(): greenelet: <greenlet.greenlet object at 0xb729cc5c>
inner(): greenelet: <greenlet.greenlet object at 0xb729ce3c>
Sleeping for 0.545666723422 seconds...
sleep(): greenelet: <greenlet.greenlet object at 0xb729ce3c>
wait_one(): greenelet: <greenlet.greenlet object at 0xb729cc5c>
done
0.545666723422
Here
Exception twisted.python.failure.Failure: <twisted.python.failure.Failure <class 'greenlet.GreenletExit'>> in <greenlet.greenlet object at 0xb729ce3c> ignored
GreenletExit did not kill <greenlet.greenlet object at 0xb729ce3c>

做了一些研究我发现:

  • 最后一行由greenlet.c记录
  • 上一行由python本身记录,因为它忽略了 del 方法中引发的异常。

我在调试时遇到了麻烦,因为我无法访问GreenletExittwisted.python.failure.Failure异常以获取其堆栈跟踪。

有没有人知道我做错了什么,或者我如何调试被抛出的异常?

另一个数据点:如果我修改wait_one()只是立即返回(并且没有注册延迟传递的任何内容),错误就会消失。 : - /

2 个答案:

答案 0 :(得分:2)

wait_one中重写您的错误回调,如下所示:

  def errback(failure):
    ## new code
    if g.dead:
        return
    ##
    if not active:
        g.throw(failure)
    else:
        reactor.callLater(0, g.throw, failure)

如果greenlet已经死亡(已完成运行),则无异常抛出异常 在它。

答案 1 :(得分:0)

mguijarr的答案解决了这个问题,但我想写下我是如何陷入这种情况的。

我有三个greenlets:

  • {main}正在运行反应堆。
  • {outer}正在运行outer()。
  • {inner}那是rrunning inner()。

当睡眠结束时,{main}切换到{inner}切换到{outer}。然后外部返回并在{inner}中引发GreenletExit。这传回了扭曲。它看到从callback()引发异常,因此调用errback()。这会尝试将异常抛入{outer}(已经退出),并且我遇到了错误。