Используйте мой собственный основной цикл в twisted

у меня есть существующая программа, которая имеет свой собственный основной цикл, и делает вычисления на основе входных данных, которые он получает, скажем, от пользователя, чтобы сделать его простым. Теперь я хочу делать вычисления удаленно, а не локально, и я решил реализовать RPCs в Twisted.

В идеале я просто хочу изменить одну из моих функций, скажем doComputation(), чтобы сделать вызов twisted для выполнения RPC, получить результаты и вернуться. Остальная часть программы должна остаться прежней. Как я могу достичь в этом, правда? Twisted захватывает основной цикл, когда я вызываю reactor.run(). Я также читал, что у вас на самом деле нет потоков в twisted, что все задачи выполняются последовательно, поэтому, похоже, я не могу просто создать LoopingCall и запустить мой основной цикл таким образом.

2 ответов


у вас есть несколько различных вариантов, в зависимости от того, какой основной цикл существующей программы.

если это mainloop из библиотеки GUI,Twisted может уже иметь поддержку для него. В таком случае, вы можете просто пойти и использовать его.

вы также можете написать свой собственный реактор. Для этого не так много отличной документации, но вы можете посмотреть, как qtreactor реализует плагин реактора внешне Витой.

вы также можете написать минимальный реактор, используя threadedselectreactor. Документация для этого также разрежена, но в wxPython, в реактор реализуется с его помощью. Лично я бы не рекомендовал этот подход, поскольку его трудно проверить и может привести к запутанным условиям гонки, но у него есть преимущество, позволяющее вам использовать почти весь сетевой код по умолчанию Twisted с тонким слоем обертывания.

если вы не действительно уверен, что вы не хотите свой doComputation чтобы быть асинхронным, и вы хотите, чтобы ваша программа блокировалась во время ожидания ответа Twisted, сделайте следующее:

  • начните крутить в другом потоке, прежде чем начнется ваш основной цикл, с чем-то вроде twistedThread = Thread(target=reactor.run); twistedThread.start()
  • создать экземпляр объекта для выполнения связи RPC (скажем,RPCDoer) в потоке собственного основного цикла, так что у вас есть ссылка на него. Убедитесь в том, чтобы на самом деле начать свою скрученную логику с reactor.callFromThread поэтому вам не нужно обертывать все его скрученные вызовы API.
  • реализовать RPCDoer.doRPC возвратить отложить, используя только скрученные вызовы API (т. е. не вызывайте существующий код приложения, поэтому вам не нужно беспокоиться о безопасности потоков для объектов приложения; pass doRPC вся информация, которая ему нужна в качестве аргументов).
  • теперь вы можете реализовать doComputation как это:

    def doComputation(self):
        rpcResult = blockingCallFromThread(reactor, self.myRPCDoer.doRPC)
        return self.computeSomethingFrom(rpcResult)
    
  • не забудьте позвонить reactor.callFromThread(reactor.stop); twistedThread.join() из процедуры выключения вашего основного цикла, иначе вы можете увидеть некоторые запутанные трассировки или сообщения журнала при выходе.

наконец, один вариант, который вы действительно должны рассмотреть, особенно в долгосрочной перспективе: сбросить существующий основной цикл и выяснить способ просто использовать Twisted. По моему опыту, это правильный ответ на 9 из 10 вопросов, подобных этому. Я не говорю, что это всегда путь - есть много случаев, когда вам действительно нужно, чтобы сохранить свой основной цикл, или когда это просто слишком много усилий, чтобы избавиться от существующей петли. Но поддержание собственного цикла-тоже работа. Имейте в виду, что Twisted loop был широко протестирован миллионами пользователей и используется в самых разных средах. Если ваш цикл также очень зрелый, это может быть не так уж важно, но если вы пишете небольшую новую программу, разница в надежность может быть значительной.


Кажется, что правильный и очень простой ответ здесь-LoopingCall:

http://www.saltycrane.com/blog/2008/10/running-functions-periodically-using-twisteds-loopingcall/

from datetime import datetime
from twisted.internet.task import LoopingCall
from twisted.internet import reactor

def doComputation():
    print "Custom fn run at", datetime.now()

lc = LoopingCall(doComputation)
lc.start(0.1)  # run your own loop 10 times a second

# put your other twisted here

reactor.run()