2016-12-08 4 views
0

Предположим, что в витой программе Python есть синхронная функция, которая занимает много времени, и это выполняется во множестве работ по разумному размеру. Если функция могла возвращать отложенные периоды, это было бы непросто, однако функция оказывается глубоко внутри некоторого синхронного кода, так что уступка отсрочек для продолжения невозможна.скручены: обработка входящих событий синхронным кодом

Есть ли способ разрешить витую рукоятку выдающихся событий, не выходя из этой функции? То есть то, что я хочу сделать что-то вдоль линий

def my_func(): 
    results = [] 
    for item in a_lot_of_items(): 
     results.append(do_computation(item)) 
     reactor.process_outstanding_events() 
    return results 

Конечно, это накладывает требования Реентерабельность по коду, но все-таки, там QCoreApplication.processEvents для этого в Qt, есть ли что-нибудь в скручены?

ответ

1

Решение, принятое некоторыми системами на основе циклов событий (по существу, решение, которое вы ссылаетесь через API QCoreApplication.processEvents Qt), состоит в том, чтобы выполнить повторный вход в основной цикл. В Twisted выражении это будет означать что-то вроде (не рабочий код):

def my_expensive_task_that_cannot_be_asynchronous(): 
    @inlineCallbacks 
    def do_work(units): 
     for unit in units: 
      yield do_one_work_asynchronously(unit) 
    work = do_work(some_work_units()) 
    work.addBoth(lambda ignored: reactor.stop()) 
    reactor.run() 

def main(): 
    # Whatever your setup is... 
    # Then, hypothetical event source triggering your 
    # expensive function: 
    reactor.callLater(
     30, 
     my_expensive_task_that_cannot_be_asynchronous, 
    ) 
    reactor.run() 

Обратите внимание, как есть дваreactor.run вызовы в этой программе. Если у Twisted был цикл событий повторного входа, этот второй вызов снова начнет вращать реактор и не вернется, пока не встретится соответствующий вызов reactor.stop. Реактор будет обрабатывать все события, о которых он знает, а не только те, которые генерируются do_work, и поэтому у вас будет такое поведение, которое вы желаете.

Для этого требуется цикл событий повторного входа, потому что my_expensive_task_... уже вызывается контуром реактора. Контур реактора находится в стеке вызовов. Затем вызывается reactor.run и петля реактора теперь находится в стеке вызовов снова. Таким образом, применяются обычные проблемы: цикл событий не может быть оставлен над состоянием в его кадре (в противном случае он может быть недействительным к моменту завершения вложенного вызова), он не может оставить свое состояние экземпляра непоследовательным во время любых вызовов другому коду и т. Д.

У витой пары нет цикла повторного входа. Это признак, который был рассмотрен и, по крайней мере в прошлом, явно отклонен. Поддержка этих функций приносит большую сложность (описанную выше) к реализации и приложения. Если цикл события повторен, то становится очень трудно избежать необходимости повторного включения всех программных кодов.Это отрицает одно из основных преимуществ кооперативного подхода к многозадачности. Twisted принимает на параллелизм (вам гарантировано, что ваши функции не будут повторно введены).

Итак, при использовании Twisted это решение отсутствует.

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

  • сделать синхронный код, способным справиться с асинхронными вещами
  • фактора дорогих частями, и вычислить их, а затем передать результат в к остальной части коды
  • запустить все этого кода, а не только вычислительно дорогой части, в другом потоке
0

Вы можете использовать deferToThread.

http://twistedmatrix.com/documents/13.2.0/core/howto/threading.html

Этот метод работает ваш расчет в отдельном потоке и возвращает отложила, который перезвонил, когда вычисление фактически закончена.

+0

это помогло бы, если бы я мог вернуть результат 'my_func', как отложенный, но, как я сказал, он похоронен глубоко в синхронном коде, и ret Урн отложен, я должен переписать все это, чтобы пузырить отложенные до самого цикла событий. – immerrr

+0

Я обновил вопрос, чтобы отразить, что время единственной итерации в порядке, а также то, что все время итерации слишком велико. – immerrr

0

Это должно сделать:

for item in items: 
    reactor.callLater(0, heavy_func, item) 

reactor.callLater должен привести вас обратно в цикл обработки событий.

0

Проблема в том, что do_heavy_computation() - это код, который блокирует, тогда выполнение не переходит к следующей функции. В этом случае используйте deferToThread или blockingCallFromThread для тяжелых расчетов. Также, если вам не нужны результаты расчета, вы можете использовать callInThread. Посмотрите на documentation on threads

+0

ОК, я вижу, как я мог спутать людей с «тяжелой» частью. обработка одного элемента в порядке, задержка действительно начинает пинать, когда их много. Позвольте мне немного изменить вопрос. – immerrr

+0

Я обновил вопрос. – immerrr

 Смежные вопросы

  • Нет связанных вопросов^_^