Реализация каналов связи между фоновыми задачами.
И так мы научились обмениваться сообщениями между фоновой задачей и основным потоком программы. А что делать, если мы захотим обменяться сообщениями между различными фоновыми задачами. Ответ прост - мы должны создать дополнительный канал связи используя систему связи OTL. Оговорюсь сразу, подробное рассмотрение системы связи OTL будет дано отдельно, здесь же речь пойдет так сказать о практической реализации.
Рассмотрим создание дополнительного канала связи на демонстрационном примере tests\8_RegisterComm. За обеспечение механизма связи (создание и поддержку канала) в OTL отвечает интерфейс IOmniTwoWayChannel. Данный интерфейс обеспечивает реализацию канала связи между двумя конечными точками (Endpoint1 – канал связи – Endpoint2). Кончные точки представлены интерфейсом IOmniCommunicationEndpoint.
В OTL уже существует класс TOmniTwoWayChannel обеспечивающий поддержку интерфейса IOmniTwoWayChannel. Вот с ним мы и будем работать.
Итак, создадим канал связи (IOmniTwoWayChannel), используя функцию CreateTwoWayChannel. Также создадим две фоновые задачи, основанные на классе TOmniWorker. Каждая задача получила одну конечную точку нового канала связи в качестве параметра конструктора класса.
test_8_RegisterComm.pas Создание дополнительного канала связи
procedure TfrmTestRegisterComm.FormCreate(Sender: TObject);
begin
FCommChannel := CreateTwoWayChannel(1024);
FClient1 := CreateTask(TCommTester.Create(FCommChannel.Endpoint1)) // 1 точка
.MonitorWith(OmniTED)
.Run;
FClient2 := CreateTask(TCommTester.Create(FCommChannel.Endpoint2)) // 2 точка
.MonitorWith(OmniTED)
.Run;
end;
Функция CreateTwoWayChannel создает объект TOmniTwoWayChannel, который отвечает за поддержку канала связи и возвращает интерфейсную ссылку на него (Более подробная информация о внутренней работе системы связи OTL представлена в разделе “Система связи”).
В обработчике нажатия кнопки “Send To Task 1” напишем код, реализующий отправку сообщения MSG_FORWARD и случайно сгенерированного числа в первую фоновую задачу.
test_8_RegisterComm.pas
procedure TfrmTestRegisterComm.btnSendTo1Click(Sender: TObject);
var
value: integer;
begin
value := Random(100);
Log(Format('Sending %d to task 1', [value]));
FClient1.Comm.Send(MSG_FORWARD, value);
end;
В обработчике другой кнопки будет похожий код, только FClient1 будет заменен на FClient2. Класс TCommTester, реализующий фоновую задачу будет иметь обработчики сообщений для MSG_FORWARD и MSG_FORWARDING.
test_8_RegisterComm.pas Класс TCommTester
TCommTester = class(TOmniWorker)
strict private
// Конечная точка (для приема и передачи сообщений)
ctComm: IOmniCommunicationEndpoint;
public
constructor Create(commEndpoint: IOmniCommunicationEndpoint);
function Initialize: boolean; override;
// Получено сообщение от основного потока
procedure OMForward(var msg: TOmniMessage); message MSG_FORWARD;
// Получено сообщение от другого фонового потока
procedure OMForwarding(var msg: TOmniMessage); message MSG_FORWARDING;
end; { TCommTester }
Код в этом методе OMForward, во-первых уведомит GUI (основной поток программы), что сообщение MSG_FORWARD было получено и во-вторых отправит сообщение MSG_FORWARDING межзадачному каналу связи.
Обработчик MSG_FORWARDING (OMForwarding) только уведомит GUI, что сообщение было получено от другой фоновой задачи.
test_8_RegisterComm.pas
procedure TCommTester.OMForward(var msg: TOmniMessage);
begin
// Уведомим основной поток
Task.Comm.Send(MSG_NOTIFY_FORWARD, msg.MsgData);
// пошлее сообщение в дополнительный канал
ctComm.Send(MSG_FORWARDING, msg.MsgData);
end;
procedure TCommTester.OMForwarding(var msg: TOmniMessage);
begin
// Уведомим основной поток, что мы получили сообщение
// от другой фоновой задачи
Task.Comm.Send(MSG_NOTIFY_RECEPTION, msg.MsgData);
end;
Взаимодействии основного GUI и двух потоков, связанных дополнительным каналом связи лучше всего понять по рисунке ниже:
Рис. Межпоточное взаимодействие с помощью дополнительного канала связи.
В следующем рисунке, Вы можете видеть, как значение 0 передалось от задачи 1 до задачи 2 и как значение 3 передалось от задачи 2 до задачи 1.
Рис. Передача сообщение от потока к потоку
Вроде просто, но не все. Основной фокус кроется в методе TCommTester.Initialize, где метод RegisterComm (ctComm) регистрирует конечную точку связи как дополнительный источник сообщений.
test_8_RegisterComm.pas
function TCommTester.Initialize: boolean;
begin
// Регистрируем конечную точку связи
Task.RegisterComm(ctComm);
Result := true;
end;
Наличия этого кода в инициализирующем методе класса фоновой задачи обеспечивает включения механизма отправки и приема сообщений, пребывающих в канал связи (ctComm) так же как это происходит с сообщениями, прибывающими в канал Comm. Фактически RegisterComm вы должны вызывать всегда, когда хотите задействовать механизм связи для дополнительного канала. Почему это так сделано и почему нужен принудительный вызов RegisterComm (и почему нет автоматической регистрации канала связи), вы узнаете более подробно далее в посте “Система связи”.
Комментариев нет:
Отправить комментарий