Используйте мой собственный основной цикл в витой - PullRequest
6 голосов
/ 22 июля 2010

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

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

Ответы [ 2 ]

8 голосов
/ 22 июля 2010

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

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

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

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

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

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

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

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

1 голос
/ 13 марта 2016

Кажется, что правильный и очень простой ответ - это 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()
...