我正在尝试使用带有绿叶的扭曲,所以我可以在不使用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 个主要部分:
- 一个
- 睡眠函数,启动一个计时器,然后切换回父绿绿。 当计时器响起时,它会切换回正在休眠的绿绿灯。
- make_async装饰器。这需要一些同步外观的代码并在绿色中运行它。IT 还会返回延迟,以便调用方可以在代码完成时注册回调。
- 一个wait_one函数,它阻止绿绿,直到 等待的延迟得到解决。
- 内部函数(包装时)返回延迟,随机休眠一段时间,然后将睡眠时间传递给延迟时间。
- 调用 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 方法中引发的异常。
我在调试时遇到了真正的麻烦,因为我无法访问GreenletExit
或twisted.python.failure.Failure
异常来获取他们的堆栈跟踪。
有没有人知道我做错了什么,或者我如何调试正在抛出的异常?
另一个数据点:如果我破解wait_one()立即返回(并且不注册任何延迟它被传递的内容),错误就会消失。
这样用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 已死(已完成运行),则无需抛出异常在其中。
mguijarr的回答解决了这个问题,但我想写下我是如何陷入这种情况的。
我有三个绿地:
- {main} 正在运行反应堆。
- {outer} that's run outer()。
- {inner} that's rrunning inner().
当睡眠结束时,{main} 切换到 {inner},后者切换到 {outer}。然后 outer 返回并在 {inner} 中引发 GreenletExit。这又回到了扭曲。它看到从 callback() 引发的异常,因此调用 errback()。 这试图将异常抛入 {outer}(已经退出),我遇到了错误。