четверг, 30 сентября 2010 г.

Использование OmniThread Libray (OTL) для создания многопоточных приложений - 11

Вызов метода по имени или по адресу

В этом разделе мы рассмотрим различные способы вызова методов фоновой задачи.
Давайте рассмотрим это на примере уже знакомого нам метода SetTimer интерфейса IOmniTaskControl, который мы вкратце рассмотрели в разделе “Двухсторонняя связь. Подход 2”. Итак, в том нашем примере мы посылали сообщение MSG_SEND_MESSAGE в фоновую задачу для последующей обработки таким способом:

  FWorker := TAsyncHello.Create('Hello');
  FHelloTask :=
    OmniEventMonitor1.Monitor(CreateTask(FWorker, 'Hello'))
    .SetTimer(1000, MSG_SEND_MESSAGE)        // ПОСЫЛАЕМ СООБЩЕНИЕ
    .SetParameter('Delay', 1000)
    .Run;
После чего в фоновой задаче мы перехватывали это сообщение обработчиком

  TAsyncHello = class(TOmniWorker)
  strict private
    aiCount  : integer;
    aiMessage: string;
  public
.  .  .
    // Наш обработчик MSG_SEND_MESSAGE
    procedure OMSendMessage(var msg: TOmniMessage); message MSG_SEND_MESSAGE;
.  .  .
  end;

В принципе все это мы уже проходили и все это нам понятно. Послали сообщение – обработали его. Однако можно переписать данный код другим способом. Дело в том, что OTL позволяет указывать имя метода или ссылку на этот метод в параметрах. На примере метода SetTimer это выглядит так:
SetTimer
    procedure SetTimer(interval_ms: cardinal; const timerMethod: pointer); overload;
    procedure SetTimer(interval_ms: cardinal; const timerMessageName: string); overload;

В первом случае Вы можете передавать указатель на метод обработчика данного сообщения, а во втором указать имя метода обработчика, которое должно быть вызвано OTL при срабатывании таймера.
Если мы перепишем задачу, то обработчик в фоновой задаче запишем так:

TAsyncHello = class(TOmniWorker)
  public
.  .  .
    // Наш обработчик MSG_SEND_MESSAGE
    procedure SendMessage;
  end;

Вызов же может быть осуществлен таким способом
Вызов метода фоновой задачи
var
  worker: IOmniWorker;
begin
  worker := TAsyncHello.Create;
  FHelloTask :=
    OmniEventMonitor1.Monitor(CreateTask(worker, 'Hello'));
    // Вызов по имени метода TAsyncHello
    FHelloTask.SetTimer(1000, 'SendMessage')
    // Или вызов по адресу метода  TAsyncHello.SendMessage
    FHelloTask.SetTimer(1000, @TAsyncHello.SendMessage);
  FHelloTask.Run;

Если вы используете вызов метода по имени, знайте,  что OTL не проверяет достоверность задания имени метода в фоновой задаче. То есть если Вы сделаете опечатку и напишете, например
  FHelloTask.SetTimer(1000, 'SendMessages') //последняя s лишняя
то OTL не найдет такой метод и будет сгенерированная ошибка.
Еще важной особенность является то, что при использовании вызова по адресу, Вы не имеете права указывать адрес метода, который не находится в фоновой задаче (потоке класса TOmniWorker). Такой вызов приведет к ошибке:
type
  TAsyncHello = class(TOmniWorker)       
  public
    procedure SendMessage;
  end;

  TfrmTest = class(TForm)
.  .  .
    procedure SendMessage;
.  .  .
  end;
.  .  .
  FHelloTask.SetTimer(1000, @TfrmTest.SendMessage); // так делать нельзя

С эти мы разобрались. До этого весь наш раздел строился на разборе метода SetTimer, что было хорошо для введения. Продолжим далее, библиотека OTL позволяет Вам вызывать любые методы фоновой задачи (а также передавать в них данные) используя для этого метод Invoke интерфейса IOmniTaskControl.
Способы вызова метода Invoke представлены далее:
Invoke
    function  Invoke(const msgMethod: pointer): IOmniTaskControl; overload;
    function  Invoke(const msgMethod: pointer; msgData: array of const): IOmniTaskControl; overload;
    function  Invoke(const msgMethod: pointer; msgData: TOmniValue): IOmniTaskControl; overload;
    function  Invoke(const msgName: string): IOmniTaskControl; overload;
    function  Invoke(const msgName: string; msgData: array of const): IOmniTaskControl; overload;
    function  Invoke(const msgName: string; msgData: TOmniValue): IOmniTaskControl; overload;

Как видите, ничего сложного в этом методе нет. На входе в первом параметре Invoke так-же принимает либо имя метода задачи, либо адрес на метод. Второй параметр служит для передачи некоторых данных. Тип TOmniValue будет более подробно рассмотрен нами в разделе “Внутренняя организация OTL”.
Чтобы закрепить на практике наши знания рассмотрим простой пример:
Вызов метода фоновой задачи из основного потока
  // наша фоновая задача
  TAsyncHello = class(TOmniWorker)
  strict private
    aiMessage: string;
  public
    function  Initialize: boolean; override;
  published
    procedure Change(const data: TOmniValue);
    procedure SendMessage;
  end;
. . .
// ФОНОВАЯ ЗАДАЧА ЗАПУЩЕНА
// Из GUI-потока мы всегда сможем вызвать любой наш метод с помощью Invoke
// FHelloTask – интерфейс управления IOmniTaskControl нашей фоновой задачи
. . .
procedure TfrmTestStringMsgDispatch.btnChangeMessageClick(Sender: TObject);
begin
   // Вызываем метод Change по имени и посылаем случайно сгенерированную строку
   FHelloTask.Invoke('Change', 'Random ' + IntToStr(Random(1234)))
   // Либо вызываем метод по адресу на него
   FHelloTask.Invoke(@TAsyncHello.Change, 'Random ' + IntToStr(Random(1234)));
end;
// Наш метод, который вызван из основного потока
procedure TAsyncHello.Change(const data: TOmniValue);
begin
  aiMessage := data;
end;

Как видите, вызвать определенный метод фоновой задачи не представляется сложным. С OTL идет демонстрационный пример tests\18_StringMsgDispatch, который прекрасно демонстрирует использование данного метода. Используя метод Invoke вы легко сможете наделять Ваши фоновые задачи многофункциональностью и гибкостью разделяя разный функциональный код в методах.

Комментариев нет:

Отправить комментарий