Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Завершение цепочки асинхронных операций


Автор: LeonidPr 28.11.2019, 13:48
Данная проблема всплыла в процессе написания десктопного приложения для опроса устройств по ModBus
Пока была задача просто опросить регистры, или записать что-то в один из них, проблем не было.
Теперь задача усложняется. 
Пусть есть некая асинхронная операция записи регистра и чтения. Т.е. мы отправляем запрос и дальше занимаемся своими вещами. Когда запрос обработан (неважно таймаут случился, или нормально устройство ответило) поток, обрабатывающий очередь запросов дернет callback.
Задача такова, что нужно составлять более сложное действие из таких асинхронных операций (например нужно настроить какой-то функционал в устройстве, записав в разрозненный набор регистров нужные значения).
Естественно оно так же должно выполняться асинхронно и по завершении уведомить об успехе или нет.
Каждый раз когда пытаюсь что-то такое изобразить все выливается в хардкод какой-то стейт машины, которая по мере завершения элементарных операций переключается между состояниями.
В общем не особо гибко.
Есть ли какие-то подходы для решения подобных задач (наверняка что-то подобное возникает при работе с БД, когда так же транзакция состоит из нескольких запросов к базе, выполняемых один за другим с контролем успешности)?
Может быть какие-то паттерны для асинхронного программирования?

Автор: xvr 28.11.2019, 16:10
Я бы делал такое на основе графа зависимостей.
  • Каждое действие оформляется некоторой процедурой + набор входных и выходных зависимостей
  • Далее в цикле извлекаются все действия, для которых входные зависимости разрешены (т.е. все данные готовы)
  • Все выбранные действия запускаются асинхронно (способ запуска может быть самый разный)
  • Когда какое то действие заканчивается все его выходные зависимости помечаются разрешёнными и цикл повторяется
  • Всё это крутится пока все действия не выполнятся

Автор: LeonidPr 28.11.2019, 17:20
Цитата(xvr @  28.11.2019,  16:10 Найти цитируемый пост)
Далее в цикле извлекаются все действия, для которых входные зависимости разрешены (т.е. все данные готовы)

Только цикл здесь получается распределенным во времени, потому что к примеру ModBus (думаю некоторые другие протоколы то же) он последовательный, т.е. пока один запрос не обработан следующий не шлём.
Ну либо набивать все запросы в очередь и по мере обработки смотреть какие зависимости разрешены.
У меня сейчас мысль сделать так, что-бы ModBus рассматривать как низкоуровневый протокол, выполняющий примитивные операции чтения/записи. А поверх этого реализовать как бы еще один слой, предоставляющий возможность собирать сложные запросы из примитивных. Как своеобразный конструктор. Получается похоже на то, что вы предложили. Т.е. сложный запрос выполнен тогда, когда все примитивные выполнены успешно. Просто, боюсь обработка ошибок опять превратится в кашу из состояний и переходов.

Автор: xvr 28.11.2019, 19:29
Цитата(LeonidPr @  28.11.2019,  17:20 Найти цитируемый пост)
 ModBus последовательный, т.е. пока один запрос не обработан следующий не шлём.

Со стороны запросов это разруливается зависимостями, со стороны ModBus'а - единственным worker'ом для ModBus'а.
Т.е. все готовые запросы сваливаются в пул, в который приходят worker'ы, извлекают свои запросы и отправляются их выполнять. Пока запрос не закончился worker за следующим не идёт. Если какая то шина позволяет сделать более одного запроса, для неё заводится несколько экземпляров worker'а

Цитата(LeonidPr @  28.11.2019,  17:20 Найти цитируемый пост)
 Просто, боюсь обработка ошибок опять превратится в кашу из состояний и переходов. 

Ошибки тоже могут быть выходными зависимостями. А их обработчики - процедурами с входными зависимостями (типа 'ошибка'). Тогда всё становится одинаковым, для ошибок ничего отдельного делать не надо (за исключением того, что  процедуры обработчики ошибок имеют право остаться невыполненными)

Автор: LeonidPr 29.11.2019, 15:25
Цитата(xvr @  28.11.2019,  19:29 Найти цитируемый пост)
Со стороны запросов это разруливается зависимостями, со стороны ModBus'а - единственным worker'ом для ModBus'а.
Т.е. все готовые запросы сваливаются в пул, в который приходят worker'ы, извлекают свои запросы и отправляются их выполнять. Пока запрос не закончился worker за следующим не идёт. Если какая то шина позволяет сделать более одного запроса, для неё заводится несколько экземпляров worker'а

Ну да, сейчас так собственно и делается, только Worker пока один.

Цитата(xvr @  28.11.2019,  19:29 Найти цитируемый пост)
Ошибки тоже могут быть выходными зависимостями. А их обработчики - процедурами с входными зависимостями (типа 'ошибка'). Тогда всё становится одинаковым, для ошибок ничего отдельного делать не надо (за исключением того, что  процедуры обработчики ошибок имеют право остаться невыполненными)

х-м-м, да, в таком ключе я как-то не подумал, спасибо!

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)