Сотрудничество в Twisted - уступить контексту исполнения - PullRequest
0 голосов
/ 18 февраля 2020

Ниже находится мой тестовый стенд

from twisted.internet import defer, task


@defer.inlineCallbacks
def foo():
    yield
    print 'after yield in foo'


@defer.inlineCallbacks
def main(reactor):
    d = foo()
    yield
    print 'after yield in main'
    yield d


task.react(main)

Я ожидаю, что оператор yield сделает функцию "выдающей контекст exeuction" (что бы это ни значило в Twisted) и позволила другому отложенному захватить управление выполнение. В этом конкретном примере я ожидаю, что main() запускает выполнение, вызывает foo(), который, заключенный в inlineCallbacks, превращается в отложенный , а затем дает выполнение, позволяющее foo() в конечном итоге начать. Тогда foo(), в свою очередь, также приводит к выполнению, поэтому в конечном итоге порядок напечатанных строк должен быть

after yield in main
after yield in foo

. По какой-то причине на выходе получается

after yield in foo
after yield in main

Что является правильным способом? реализовать совместную многозадачность в Twisted и передать контекст выполнения go другому, отложенному в строке?

1 Ответ

1 голос
/ 19 февраля 2020

Я ожидаю, что оператор yield заставит функцию «выдавать контекст exeuction» (что бы это ни значило в Twisted) и позволил другому отложенному исполнить выполнение.

От ваши наблюдения, я так понимаю, вы уже разочаровались в этом ожидании. Чтобы быть совершенно ясным, это не то, что yield делает в функции генератора, украшенной inlineCallbacks.

То, что yield делает в такой функции, это передает значение батуту. Если значение является Отложенным, батут приостанавливает работу генератора, пока Отложенный не сработает. Если значение не является отложенным, или когда отложенное срабатывает со значением, генератор возобновляет работу с отправленным ему значением.

Итак, поскольку yield совпадает с yield None и None - это не Deferred, эти yield выражения - просто дорогой способ сказать None.

Это также дает вам некоторое представление о том, как добиться того, о чем вы говорите. Чтобы приостановить выполнение, выдайте Deferred, который не запускается до тех пор, пока вы не захотите возобновить выполнение.

Как правильно реализовать совместную многозадачность в Twisted и разрешить контекст выполнения go другому отложенному в строке?

Есть много возможных правильных способов сделать это. Особенности зависят в большей степени от ваших конкретных c требований к приложениям.

К сожалению, есть несколько легко объяснимых глупых ответов, которые могут показаться правильными, но, вероятно, в конечном итоге приведут к снижению производительности и затратному обслуживанию. Например, вы можете приостановить «итерацию реактора» (если такая вещь существует, что может быть меньше, чем вы думаете). Для этого выведите twisted.internet.task.deferLater(reactor, 0.0, lambda: None). Это выражение создает Deferred, которое срабатывает не раньше, чем «ноль секунд» с этого момента, но также с ограничением, что оно не срабатывает вправо сейчас.

Однако, это позволяет всему Реализация реактора имеет шанс выполнить работу до возобновления работы вашего генератора, даже если нет никакой другой работы. Таким образом, вы платите большую стоимость процессора за возможность сотрудничества. Кроме того, это затрудняет тестирование функции путем введения взаимодействий по расписанию.

Альтернатива, которую Twisted предлагает попытаться справиться с некоторыми из этих трудностей, - это twisted.internet.task.cooperate, что понижает inlineCallbacks в пользу голого генераторы. Они действительно используют yield, чтобы предложить точки приостановки, но делают это таким образом, чтобы не дать всему реактору шанса запуститься, прежде чем возобновить работу. Это решает некоторые проблемы с процессором и, возможно, некоторые трудности с обслуживанием, хотя в конечном итоге оно все еще вводит некоторые временные зависимости. Однако, по крайней мере, возможно работать на чистых генераторах без , включая cooperate, что в некоторой степени уменьшает эту трудность (сравните это с inlineCallbacks, где идиоматически написанный код разрушает базовый генератор и предлагает только функция, которая возвращает Deferred для тестирования).

...