4

我目前正在使用 Twisted 在回调内的 for 循环中重复一项任务,但如果用户通过 Ctrl-C 发出 KeyboardInterrupt,我希望反应器在回调(一个)中中断循环。根据我的测试,反应器仅在回调结束时停止或处理中断。

有没有办法在回调运行过程中向回调或错误处理程序发送 KeyboardInterrupt?

干杯,

克里斯

#!/usr/bin/env python

from twisted.internet import reactor, defer


def one(result):
    print "Start one()"
    for i in xrange(10000):
        print i
    print "End one()"
    reactor.stop()


def oneErrorHandler(failure):
    print failure
    print "INTERRUPTING one()"
    reactor.stop()    


if __name__ == '__main__':

    d = defer.Deferred()
    d.addCallback(one)
    d.addErrback(oneErrorHandler)
    reactor.callLater(1, d.callback, 'result')

    print "STARTING REACTOR..."
    try:
        reactor.run()
    except KeyboardInterrupt:
        print "Interrupted by keyboard. Exiting."
        reactor.stop()
4

2 回答 2

8

我得到了这个工作的花花公子。触发的 SIGINT 为我的代码中的任何正在运行的任务设置一个运行标志并另外调用reactor.callFromThread(reactor.stop)以停止任何扭曲的运行代码:

#!/usr/bin/env python

import sys
import twisted
import re
from twisted.internet import reactor, defer, task
import signal


def one(result, token):
    print "Start one()"
    for i in xrange(1000):
        print i
        if token.running is False:
            raise KeyboardInterrupt()
            #reactor.callFromThread(reactor.stop) # this doesn't work
    print "End one()"

def oneErrorHandler(failure):
    print "INTERRUPTING one(): Unkown Exception"
    import traceback
    print traceback.format_exc()
    reactor.stop()

def oneKeyboardInterruptHandler(failure):
    failure.trap(KeyboardInterrupt)
    print "INTERRUPTING one(): KeyboardInterrupt"
    reactor.stop()

def repeatingTask(token):
    d = defer.Deferred()
    d.addCallback(one, token)
    d.addErrback(oneKeyboardInterruptHandler)
    d.addErrback(oneErrorHandler)
    d.callback('result')

class Token(object):
    def __init__(self):
        self.running = True

def sayBye():
    print "bye bye."


if __name__ == '__main__':

    token = Token()

    def customHandler(signum, stackframe):
        print "Got signal: %s" % signum
        token.running = False                # to stop my code
        reactor.callFromThread(reactor.stop) # to stop twisted code when in the reactor loop
    signal.signal(signal.SIGINT, customHandler)

    t2 = task.LoopingCall(reactor.callLater, 0, repeatingTask, token)
    t2.start(5) 

    reactor.addSystemEventTrigger('during', 'shutdown', sayBye)

    print "STARTING REACTOR..."
    reactor.run()
于 2010-11-10T08:51:33.913 回答
6

这是为了避免(半)抢占,因为 Twisted 是一个协作式多任务系统。Ctrl-C 在 Python 中通过解释器在启动时安装的 SIGINT 处理程序进行处理。处理程序在调用时设置一个标志。在执行每个字节码后,解释器检查标志。如果已设置,则此时会引发 KeyboardInterrupt。

反应器安装自己的 SIGINT 处理程序。这取代了解释器处理程序的行为。反应堆的处理程序启动反应堆关闭。由于它不会引发异常,因此它不会中断正在运行的任何代码。循环(或其他)结束,当控制权返回反应堆时,关闭继续进行。

如果您希望 Ctrl-C(即 SIGINT)引发 KeyboardInterrupt,那么您可以使用信号模块恢复 Python 的 SIGINT 处理程序:

signal.signal(signal.SIGINT, signal.default_int_handler)

但是请注意,如果您在 Twisted 的代码运行时发送 SIGINT,而不是您自己的应用程序代码,则行为未定义,因为 Twisted 不希望被 KeyboardInterrupt 中断。

于 2010-11-08T17:40:51.043 回答