如何从Twisted的deferToThread API中延迟添加延迟?

时间:2011-04-18 09:37:38

标签: python twisted

from twisted.internet import reactor
from twisted.internet import threads
from twisted.internet import defer
import time

def worker(arg):
    print 'Hello world'
     time.sleep(10)
    return 1

def run():
    print 'Starting workers'
    l = []
    for x in range(2):
        l.append(threads.deferToThread(worker, x))
    return defer.DeferredList(l)

def res(results):
    print results
    reactor.stop()

d = run()
d.addCallback(res)
reactor.run()

如何通过超时停止工作人员?

4 个答案:

答案 0 :(得分:5)

除非与您合作,否则不能中断线程。 time.sleep(10)不会合作,所以我认为你不能打断这个工人。如果你有另一种具有多个离散阶段的工人,或者在一些任务的循环中操作,那么你可以这样做:

def worker(stop, jobs):
    for j in jobs:
        if stop:
            break
        j.do()

stop = []
d = deferToThread(worker)

# This will make the list eval to true and break out of the loop.
stop.append(None)

这也不是Twisted特定的。这就是线程在Python中的工作方式。

答案 1 :(得分:3)

虽然可能无法中断线程,但可以通过cancel函数停止延迟,我认为这在Twisted 10.1.0及更高版本中可用。

如果Deferred在一段时间后没有触发,我已经使用以下类来使Deferreds回调特定函数。对于那些与OP主题具有相同问题的人来说,这可能是有用的。

编辑:正如下面的评论所建议的那样,最好不要继承defer.Deferred。因此,我已经将代码更改为使用可实现相同效果的包装器。

class DeferredWrapperWithTimeout(object):
    '''
    Holds a deferred that allows a specified function to be called-back
    if the deferred does not fire before some specified timeout.
    '''
    def __init__(self, canceller=None):
        self._def = defer.Deferred(canceller)

    def _finish(self, r, t):
        '''
        Function to be called (internally) after the Deferred
        has fired, in order to cancel the timeout.
        '''
        if ( (t!=None) and (t.active()) ):
            t.cancel()
        return r

    def getDeferred(self):
        return self._def

    def addTimeoutCallback(self, reactr, timeout,
                           callUponTimeout, *args, **kw):
        '''
        The function 'callUponTimeout' (with optional args or keywords)
        will be called after 'timeout' seconds, unless the Deferred fires.
        '''

        def timeoutCallback():
            self._def.cancel()
            callUponTimeout(*args, **kw)
        toc = reactr.callLater(timeout, timeoutCallback)
        return self._def.addCallback(self._finish, toc)
超时之前

示例回调:

from twisted.internet import reactor

from DeferredWithTimeout import *

dw = DeferredWrapperWithTimeout()
d  = dw.getDeferred()

def testCallback(x=None):
    print "called"

def testTimeout(x=None):
    print "timedout"

d.addCallback(testCallback)
dw.addTimeoutCallback(reactor, 20, testTimeout, "to")
reactor.callLater(2, d.callback, "cb")
reactor.run()

打印“被叫”,别无其他。

回调前

示例超时:

from twisted.internet import reactor

from DeferredWithTimeout import *

dw = DeferredWrapperWithTimeout()
d  = dw.getDeferred()

def testCallback(x=None):
    print "called"

def testTimeout(x=None):
    print "timedout"

d.addCallback(testCallback)
dw.addTimeoutCallback(reactor, 20, testTimeout, "to")
reactor.run()

20秒后打印“timedout”,没有别的。

答案 2 :(得分:0)

好吧,我的答案不是关于线程,但正如所说,你可以将超时功能作为一个单独的帮助器实现:

from twisted.internet import defer

def add_watchdog(deferred, timeout=0.05):

    def callback(value):
        if not watchdog.called:
            watchdog.cancel()
        return value

    deferred.addBoth(callback)

    from twisted.internet import reactor
    watchdog = reactor.callLater(timeout, defer.timeout, deferred)

d = defer.Deferred()
add_watchdog(d)

然后,如果需要,您可以将defer.TimeoutError陷入延迟的错误中。

答案 3 :(得分:0)

我们这样做是使用装饰器。此方法的优点是在达到超时时取消延迟。这应该以某种方式成为Twisted库imho的一部分

FacebookClient client = new FacebookClient();

client.AppId = appId;
client.AppSecret = appSecret;

dynamic result = client.Get("oauth/access_token", new
{
    client_id = appId,
    client_secret = appSecret,
    grant_type = "client_credentials"
});
//client.AccessToken = "";

var accessToken = result.access_token;
client.AccessToken = accessToken;

dynamic parameters = new ExpandoObject();
parameters.client_id = appId;
parameters.redirect_uri = "https://www.facebook.com/connect/login_success.html";
parameters.response_type = "token";
parameters.display = "popup";
parameters.scope = "publish_actions";

var login = client.GetLoginUrl(parameters);

// AccessToken token;
// var accessToken = client.AccessToken;

dynamic result2 = client.Get("me");