1

私はグリーンレットでツイストを使用しようとしているので、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 は deferred も返すため、コードの完了時に呼び出し元がコールバックを登録できます。
  • 待機中の遅延が解決されるまで greenlet をブロックする wait_one 関数。
  • (ラップされたときに) deferred を返す内部関数は、ランダムな時間スリープし、スリープした時間を deferred に渡します。
  • inner() を呼び出す外部関数は、それが戻るのを待ってから、戻り値を出力します。

このコードを実行すると、次の出力が得られます (最後の 2 行のエラーに注意してください)。

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 によって記録されます
  • 前の行は、 delメソッドで発生した例外を無視しているため、python 自体によってログに記録されます。

GreenletExitまたはtwisted.python.failure.Failure例外にアクセスしてスタックトレースを取得できないため、これをデバッグするのに本当に問題があります。

私が間違っていること、またはスローされている例外をデバッグする方法を知っている人はいますか?

もう1つのデータポイント:wait_one()をハックしてすぐに返すと(そして渡された遅延に何も登録しないように)、エラーはなくなります。:-/

4

2 に答える 2

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 が停止している (実行が終了している) 場合、例外をスローしても意味がありません。

于 2013-10-26T11:40:52.827 に答える
0

mguijarr の回答で問題は解決しましたが、この状況にどのように陥ったかを書き留めたいと思いました。

私は3つのグリーンレットを持っています:

  • リアクターを実行している {main}。
  • outer() を実行している {outer}。
  • inner() を実行している {inner}。

スリープが終了すると、{main} は {inner} に切り替わり、{outer} に切り替わります。次に Outer が返され、{inner} で GreenletExit が発生します。これはツイストに戻ります。callback() から例外が発生していることを確認し、errback() を呼び出します。これは例外を {outer} (既に終了している) にスローしようとしましたが、エラーが発生しました。

于 2013-10-26T13:01:52.380 に答える