витая: как элегантно обмениваться между кодом реактора и потоком кода? - PullRequest
2 голосов
/ 12 августа 2010

У меня есть клиент, подключенный к серверу с использованием витой.У клиента есть поток, который потенциально может делать вещи в фоновом режиме.Когда реактор закрывается, я должен:

1) check if the thread is doing things
2) stop it if it is

Какой элегантный способ сделать это?Лучшее, что я могу сделать, - это какая-то запутанная вещь, например:

def cleanup(self):
    isWorkingDF = defer.Deferred()
    doneDF = defer.Deferred()

    def checkIsWorking():
        res = self.stuff.isWorking() #blocking call
        reactor.callFromThread(isWorkingDF.callback, res)

    def shutdownOrNot(isWorking):
        if isWorking:
            #shutdown necessary, shutdown is also a blocking call
            def shutdown():
                self.stuff.shutdown()
                reactor.callFromThread(doneDF, None)
            reactor.callInThread(shutdown)                
        else:
            doneDF.callback(None) #no shutdown needed

    isWorkingDF.addCallback(shutdownOrNot)

    reactor.callInThread(checkIsWorking)

    return doneDF

Сначала мы проверим, работает ли она вообще.Результат этого обратного вызова переходит в rescallback, который либо выключается, либо не срабатывает, а затем запускает doneDF, который искажается и ждет закрытия.

Довольно испортился, а!Есть ли способ лучше?

Может быть, связанный с этим вопрос, есть ли более элегантный способ связать обратные вызовы друг с другом?Я мог видеть, что мне нужно сделать больше кода для очистки после того, как это будет сделано, так что тогда мне придется сделать другой done отложенный, и заставить текущий doneDF вызвать обратный вызов, который делает вещи, затем вызывает, что done отложил..

Ответы [ 3 ]

6 голосов
/ 16 сентября 2010

Ах, настоящий ответ - использовать defer.inlineCallbacks декоратор.Код выше теперь становится:

@defer.inlineCallbacks
def procShutdownStuff(self):
    isWorking = yield deferToThread(self.stuff.isWorking)

    if isWorking:
        yield deferToThread(self.stuff.shutdown)

def cleanup(self):
    return self.procShutdownStuff()
5 голосов
/ 12 августа 2010

Вы можете несколько упростить это, используя deferToThread вместо пар callInThread / callFromThread:

from twisted.internet.threads import deferToThread

def cleanup(self):
    isWorkingDF = deferToThread(self.stuff.isWorking)

    def shutdownOrNot(isWorking):
        if isWorking:
            #shutdown necessary, shutdown is also a blocking call
            return deferToThread(self.stuff.shutdown)

    isWorkingDF.addCallback(shutdownOrNot)

    return isWorkingDF

deferToThread - это просто хорошая оболочка для той же логики потоков, которую вы дважды реализовали в своей версии функции.

0 голосов
/ 12 августа 2010

Если программа завершает работу после того, как вы выключаете реактор, вы можете сделать поток потоком демона.Это автоматически завершится, когда прекратятся все потоки, не являющиеся демонами.Просто установите daemon = True на объекте потока перед вызовом start ().

Если это нежизнеспособно, например, поток должен выполнить очистку ресурса, прежде чем он выйдет, тогда вы могли бы взаимодействовать между реактором и потокомочередь.Выдвиньте работу, которую нужно выполнить, на объект Queue, и поток вытянет его и сделает это.Имейте специальный токен FINISH (или просто None), чтобы указать, что поток должен завершиться.

...