tgoop.com/cxx95/110
Last Update:
#theory
Замороженные корутины
Am Ende bleib ich doch alleine
Но в конце я остаюсь один
Die Zeit steht still und mir ist kalt
Время остановилось, и мне холодно
Этот пост больше теоретический, чем практический - просто эта тема сложная, чтобы даже иметь какой-то minimal viable product
Я много работал с двумя фреймворками, которые со всеми плюсами имели в некоторых местах очень неудобный процесс разработки:
"Таска" это такая микро-программа на Python, которая должна запускаться по какому-то триггеру и делать что-то обычно небольшое: отправка email, создание тикетов, http-запрос в сервис, и прочее
Иногда таска может быть посложнее - например, создать и запустить другие таски и ждать их выполнения. В таких случаях исходная таска "замораживается" (переходит в состояние "Wait") и прекращает свою работу, а после финиширования дочерней таски возобновляет работу, снова на каком-нибудь свободном сервере
Есть проблема - система не может как бы "продолжить" код таски с той точки, где было ожидание таски. Был придуман нереальный костыль, который выглядит примерно так:
def on_execute(self):Сервис сохраняет "контекст" между разными запусками таски, а метод
if not self.Context.config_stage:
self.Context.config_stage = True
param1 = ...
param2 = ...
task = sdk.CreateTask(param1, param2, ...) # создаем дочернюю таску №1
raise sdk.WaitTask(task)
if not self.Context.run_stage:
self.Context.run_stage = True
param = ...
task = sdk.CreateTask(param, ...) # создаем дочернюю таску №2
raise sdk.WaitTask(task)
on_execute
каждый раз запускается сначала ♻️В "контексте" можно ставить флажки, чтобы имитировать разные стадии таски, еще туда можно сохранять списки/строки/числа
Наконец, оповещение сервису о том, что текущей таске надо дождаться другую таску, делается через бросание исключения (
raise
), чтобы это "прорвалось" за пределы def on_execute
(удобнее, чем просто return
, если вызываются всякие вложенные методы) При таком подходе надо очень аккуратно писать код и много думать, если таске нужно делать что-то немного нетривиальное: например запуск дочерних тасок в цикле, пока одна из них не завершится успешно
По ссылке открыто описано что такое Apphost - фреймворк для микросервисов (на Python или C++).
Он решает многие старые проблемы, но появляется новая проблема - теперь программа не может просто так сделать HTTP-запрос.
Нужно разбивать всю программу на микроскопические куски кода, которые принимают некие "входящие" объекты и формируют "исходящие", но не более того. Все походы во внешние сервисы делает Apphost. Эти куски кода выполняются на разных серверах.
Та часть, которая раньше делалась мгновенно, сейчас делается в 10 раз медленнее из-за настройки конфигов, жесткого обдумывания "графа" выполнения, и так далее.
Неприятности в сервисах, указанных выше, теоретически можно решить через корутины. Я когда-то писал о них в канале. Это функции, которые могут приостанавливать свое выполнение, а затем продолжать с той же точки.
На самом деле, проще всего изучать корутины в простых языках - здесь статья про корутины в Lua. Общий подход не меняется.
Потом здесь можно почитать, как корутины реализованы в C++ (начиная с C++20) - это очень сложные статьи, их можно читать несколько недель, зато появляется максимальное понимание, как компилятор преобразовывает код.
Суть идеи, которая решит проблемы, которые я описал - замороженные корутины: программа выполняется на одном сервере, потом останавливается, ожидает возобновления, и выполняется на каком-нибудь другом сервере с той точки, где была остановлена
ПРОДОЛЖЕНИЕ В КОММЕНТАРИЯХ