Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: ssoft от Август 09, 2019, 09:10



Название: Паттерн Active оbject
Отправлено: ssoft от Август 09, 2019, 09:10
Существует такой паттерн, как Active object, который предназначен для организации асинхронного вызова методов целевого класса путем формирования поверх него обертки.
В недрах самой обертки присутствует планировщик задач, поток исполнения и т.п.

Если интерфейс класса имеет, например, вид

Код
C++ (Qt)
class Passive // Implementation
{
public:
   Result doSomething ();
}
 

то можно сформировать обертку вида

Код
C++ (Qt)
class Active // Proxy
{
public:
   Future< Result > doSomething ();
};
 

В этом кратком обзоре все понятно. Метод вызывает законченное действие, ждем результат завершения на Future.

Но далее возник вопрос - Что если метод провоцирует цепочку действий (сценарий) или запускает какой-то событийный цикл (прием данных, например)?
При этом важным является не только факт завершения вызываемого метода (как у Future) , но и порядок происходящих событий (стадии сценария, пакеты принимаемых данных и т.п.).

Реализация для этого случая может выглядеть так

Код
C++ (Qt)
class Active
{
public:
   Future< Result > doSomething ();
   //...
   Events takeEvents ();    
};
 

В этом случае необходимо периодически забирать события у объекта. Есть сомнения в таком решении, так как, если не забрать события, то они могут копиться бесконечно.

Другой вариант - отдавать события через callback, но это требует внешних средств синхронизации со стороны пользователя Активным объектом, и в этом случае пользователь имеет возможность затормозить или вообще заблокировать очередь задач Активного объекта.

Может есть другие варианты? Может требуется другой паттерн? Что посоветуете?


Название: Re: Паттерн Active оbject
Отправлено: Igors от Август 09, 2019, 21:00
Ну оснований что-то советовать пока никаких  :) Да, Вы полностью "абстрагировали" задачу, исключили всякую "специфику" которая вызывает (ненужные) вопросы. Но при этом довольно сложно за что-то зацепиться, понять в чем проблема и чего нужно добиться.

Насколько мне известно, упомянутый паттерн весьма общий - ну контроллер асмнхронки - вот собсно и все.

Но далее возник вопрос - Что если метод провоцирует цепочку действий (сценарий) или запускает какой-то событийный цикл (прием данных, например)?
При этом важным является не только факт завершения вызываемого метода (как у Future) , но и порядок происходящих событий (стадии сценария, пакеты принимаемых данных и т.п.).
В этом случае необходимо периодически забирать события у объекта.
...
Другой вариант - отдавать события через callback,
Может точнее "фильтровать события" (контроллером)? Ну почему нет, это вполне возможно. Но плохо это или хорошо - "зависит от задачи" (любимое выражение всех сачков  :))


Название: Re: Паттерн Active оbject
Отправлено: ssoft от Август 09, 2019, 23:16
Давайте конкретизируем на примере известной задачи).
Рассмотрим подключение tcp клиента к серверу.
Можно обернуть реализацию сокета Активным объектом ("контроллером асинхронки")

Код
C++ (Qt)
class TcpClient
{
...
   Future< bool > connect ();
...
};
 

Кроме факта подключения/не подключения, получаемого через Future, имеются события изменения состояния TcpClient
По аналогии с Qt, пусть это будет:
Код:
UnconnectedState
HostLookupState
ConnectingState
ConnectedState

Каким лучше способом получить данные события от TcpClient?


Название: Re: Паттерн Active оbject
Отправлено: Igors от Август 10, 2019, 01:42
Давайте конкретизируем на примере известной задачи).
Рассмотрим подключение tcp клиента к серверу.
Мне эта задача незнакома, с сетью практически не работаю

Каким лучше способом получить данные события от TcpClient?
Наверное как TcpClient (контроллер) получит события изменения статуса. Ну напр связать любимыми слот/сигнал вениками :)


Название: Re: Паттерн Active оbject
Отправлено: Old от Август 10, 2019, 10:45
В этом случае необходимо периодически забирать события у объекта. Есть сомнения в таком решении, так как, если не забрать события, то они могут копиться бесконечно.
Это решение не информативно для клиента объекта. Он не может отслеживать момент смены состояния, ему доступно только список того, что уже произошло с объектом. Но клиенту чаще важней вовремя получать информацию о смене состояния.
А накопление состояний это вопрос решаемый, например, можно очищать список состояний при запуске новой операции.

Другой вариант - отдавать события через callback, но это требует внешних средств синхронизации со стороны пользователя Активным объектом, и в этом случае пользователь имеет возможность затормозить или вообще заблокировать очередь задач Активного объекта.
Это традиционное решение для таких случаем.
Да, клиент должен понимать, что callback-функция вызывается из контекста исполнителя операции, и для корректной обработки применять возможность своего окружения. Например, класть сообщение о смене состояния в очередь сообщений для дальнейшей обработки и обрабатывать их в своих контекстах.
Затормозить или заблокировать это право клиента. Ему можно даже дать возможность штатно прервать операцию посередине. :)
Это все уже проблемы клиента.


Название: Re: Паттерн Active оbject
Отправлено: ssoft от Август 12, 2019, 07:37
Мне эта задача незнакома, с сетью практически не работаю

Вместо событий сети, могут быть события GUI (мышиные клики или перемещения, например), или события изменения дерева графических узлов, или события генерации каких-либо данных. Принцип работает в любом случае.

Наверное как TcpClient (контроллер) получит события изменения статуса. Ну напр связать любимыми слот/сигнал вениками :)

Это собственно и есть вариация callback).


Название: Re: Паттерн Active оbject
Отправлено: ssoft от Август 12, 2019, 08:46
Это решение не информативно для клиента объекта. Он не может отслеживать момент смены состояния, ему доступно только список того, что уже произошло с объектом.

Согласен), это собственно и смущает.

... Но клиенту чаще важней вовремя получать информацию о смене состояния ...

Клиент объекта и сам объект по сути контекстно независимы. Понятия одновременности, вовремя и т.п. для них относительны.
Здесь, скорее всего, уместен термин, "своевременно", а не "вовремя". Хотя и так понятно о чем речь).

Это традиционное решение для таких случаев ...

С "традиционным решением" не согласен. Бывает по разному. Например, для сети на posix, традиционным является как раз периодический опрос наличия данных ::select, и вычитывания всего, что пришло ::recv.
Или для любого другого межпроцессного взаимодействия, приходится периодически опрашивать какой-нибудь обобщенный ресурс (память, файлы и т.п.).

Но соглашусь, что реализация через callback является предпочтительнее, когда между производителем и потребителем можно организовать прямые взаимосвязи.
Тогда и потребителей может быть много, и каждый из них может сам решить, обрабатывать события списком или по одному и каким образом.
По-началу почему-то показалось, что существует какая-то разница между событиями, порождаемыми Активным объектом, и событиями, порождаемыми каким-то произвольным генератором. Но похоже, что нет).