Twisted + qtreactor: Как выполнить очистку после закрытия последнего окна? - PullRequest
1 голос
/ 28 марта 2011

У меня есть приложение Twisted / PyQt, которое (помимо прочего) подключается к группе удаленных ресурсов.Когда пользователь закрывает окно, я хочу закрыть все соединения, по возможности чисто, принудительно, если нет.

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

Вот код моего приложения:

# Create app and connect the Twisted/Qt reactors
app = QApplication(sys.argv)
qtreactor.qt4reactor.install()

# Shutdown Twisted when window is closed
@defer.inlineCallbacks
def stop():
    print "="*40, "Closing connections..."
    yield closeConnections()
    print "="*40, "closed."

    print "="*40, "Stopping reactor..."
    reactor.stop()
    print "="*40, "stopped."
app.connect(app, SIGNAL("lastWindowClosed()"), stop)

reactor.runReturn()
rc = app.exec_()
exit(rc)

А вот урезанная версия моего кода очистки:

@defer.inlineCallbacks
def closeConnections():
    for connection in connections:
        print "Closing connection #%s" % connection
        yield threads.deferToThread(popen("/foo/bar/cleanup %s" % connection))
        print "Connection closed."

Первыйоператор print достигнут, и команда выполнена, но я никогда не получаю второй, и при этом я не выполняю цикл for несколько раз.

Правильно ли выполнен мой анализ?Проблема в том, что реактор уже остановлен, поэтому я никогда не получаю ответ от threads.deferToThread?Или есть другая проблема?Кроме того, как мне это исправить?

Спасибо, Джонатан

1 Ответ

2 голосов
/ 28 марта 2011

Я не знаю точно, когда этот сигнал lastWindowClosed () срабатывает. Однако, даже если он срабатывает достаточно рано, до того, как реактор остановится (что мешает вам делать то, что вы хотите сделать), я уверен, что PyQt не знает, что делать с отложенным, которое возвращается вашим stop функция. Это означает, что процесс выключения будет продолжаться весело, пока ваш асинхронный код очистки попытается запустить. Скорее всего, завершение работы GUI завершится до того, как отключится ваша сеть.

Итак, используйте reactor.addSystemEventTrigger('before', 'shutdown', stop) вместо. Я не знаю, будет ли это работать немного раньше или чуть позже, чем lastWindowClosed () , но он будет работать достаточно рано, чтобы реактор все еще можно было использовать, и он обратит внимание на возврат отложенных функций. Отключение будет приостановлено, фактически, до тех пор, пока не начнется отсрочка. Это дает вам все время, необходимое для очистки.

Отдельно от всего этого не следует делать threads.deferToThread(popen("/foo/bar/cleanup %s" % connection)):

  • Вам нужно передать вызываемое значение deferToThread, а не результат вызова вызываемого. Как написано, ваш код запускается в потоке реактора и передает файловый объект вызываемому потоку (что, конечно, не имеет смысла)
  • Смешивать потоки и дочерние процессы очень сложно. Вы можете сойти с рук большую часть времени, я не знаю.
  • reactor.spawnProcess позволит вам запускать дочерний процесс без блокировки, без потоков и не беспокоясь о смешивании потоков и процессов. См. Также twisted.internet.utils.getProcessOutput, если вам не нужны все функции spawnProcess (которые, как вам кажется, не нужны).
...