Как работает Python Twisted Reactor?

Недавно я погружался в файлы Twisted. Из того, что я собрал, основой функциональности Twisted является результат цикла события, называемого "Реактор". Реактор прослушивает определенные события и отправляет их зарегистрированным функциям обратного вызова, которые были разработаны для обработки этих событий. В книге есть некоторый псевдокод, описывающий, что делает Реактор, но у меня проблемы с пониманием этого, это просто не имеет никакого смысла для меня.

 while True:
     timeout = time_until_next_timed_event()
     events = wait_for_events(timeout)
     events += timed_events_until(now())
     for event in events:
         event.process()

Что это значит?

Ответ 1

Если это не очевидно, он назвал реактор, потому что он реагирует на вещи. Цикл - это то, как он реагирует.

Одна строка за раз:

while True:

Это не на самом деле while True; это больше похоже на while not loop.stopped. Вы можете вызвать reactor.stop(), чтобы остановить цикл, и (после выполнения некоторой логики отключения) цикл будет фактически завершен. Но это изображается в примере как while True, потому что, когда вы пишете долгоживущую программу (как часто вы с Twisted), лучше предположить, что ваша программа будет либо сбой, либо запуск навсегда, и что "чистое выключение" на самом деле не вариант.

     timeout = time_until_next_timed_event()

Если бы мы немного расширили этот расчет, это могло бы иметь больше смысла:

def time_until_next_timed_event():
    now = time.time()
    timed_events.sort(key=lambda event: event.desired_time)
    soonest_event = timed_events[0]
    return soonest_event.desired_time - now

timed_events - список событий, запланированных с помощью reactor.callLater; то есть функции, которые приложение попросило Twisted для запуска в определенное время.

     events = wait_for_events(timeout)

Эта строка - это "волшебная" часть Twisted. Я не могу расширить wait_for_events в общем виде, потому что его реализация зависит от того, как операционная система делает нужные события доступными. И, учитывая, что операционные системы являются сложными и хитрыми зверями, я не могу расширять его определенным образом, сохраняя при этом достаточно простым для ответа на ваш вопрос.

То, что эта функция предназначена для обозначения, заключается в том, чтобы спросить операционную систему или оболочку Python вокруг нее блокировать до тех пор, пока один или несколько ранее зарегистрированных у него объектов - как минимум, такие как порты прослушивания и установленные соединения, но также, возможно, такие вещи, как кнопки, на которые можно щелкнуть - "готов к работе". Работа может считывать некоторые байты из сокета, когда они поступают из сети. Работа может записывать байты в сеть, когда буфер опустошается достаточно для этого. Это может быть прием нового соединения или использование закрытого. Каждое из этих возможных событий - это функции, которые реактор может вызвать ваши объекты: dataReceived, buildProtocol, resumeProducing и т.д., Которые вы узнаете, если вы пройдете полный учебник Twisted.

Как только у нас есть список гипотетических "событийных" объектов, каждый из которых имеет мнимый метод "process" (точные названия методов в реакторе различаются только из-за несчастных случаев в истории), мы затем вернитесь к работе со временем:

     events += timed_events_until(now())

Во-первых, это предполагает, что events представляет собой просто list абстрактного класса Event, который имеет метод process, который должен заполнить каждый конкретный тип события.

В этот момент цикл "проснулся", потому что wait_for_events остановил блокировку. Тем не менее, мы не знаем, сколько запланированных событий мы могли бы выполнить в зависимости от того, как долго он "спит". Возможно, мы спали за полный тайм-аут, если ничего не было, но если бы было много соединений, мы могли бы спать практически без времени. Поэтому мы проверяем текущее время ( "now()" ) и добавляем к списку событий, которые нам нужно обработать, каждое событие с тегом desired_time, которое находится в или перед этим текущим временем.

Наконец,

     for event in events:
         event.process()

Это просто означает, что Twisted просматривает список вещей, которые он должен делать, и делает их. В действительности, конечно, он обрабатывает исключения вокруг каждого события, и конкретная реализация реактора часто просто обращается непосредственно к обработчику событий, а не создает объект Event, чтобы записать работу, которая должна быть выполнена первой, но концептуально это это то, что происходит. event.process здесь может означать вызов socket.recv(), а затем yourProtocol.dataReceived с результатом, например.

Надеюсь, это расширенное объяснение поможет вам обдумать это. Если вы хотите узнать больше о Twisted, работая над этим, я бы посоветовал вам присоединиться к списку рассылки, перейти в IRC канал, #twisted, чтобы говорить о приложениях или #twisted-dev для работы с самим витой, как на Freenode.

Ответ 2

Я попытаюсь уточнить:

  • Программа обеспечивает управление и переходит в режим ожидания при ожидании событий. Полагаю, самая интересная часть здесь - событие. Событие: по внешнему запросу (получение сетевого пакета, щелчок по клавиатуре, таймер, другой программный вызов), программа получает управление (в некоторых других потоках или в специальной рутине). Так или иначе спать в wait_for_events прерывается, а wait_for_events возвращается.

  • В этом случае управления обработчик события хранит информацию об этом событии в некоторой структуре данных, событиях, которые позже используются для того, чтобы что-то делать с событиями (event- > process). Может произойти не только одно, но и много событий за время между входом и выходом wait_for_events, все они должны быть обработаны. Процедура event- > process() является настраиваемой и обычно должна вызывать интересный пользовательский скрученный код.