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

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

Односторонняя связь.

Конечно, для серьезных приложений этот тривиальный код не годится, но лиха беда начало. Далее мы рассмотрим пример, в котором происходит взаимодействие фонового потока с его владельцем. Для начала создадим одностороннуюю связь.

Надеюсь, вы уже установили пакет OmniThreadLibrary в IDE. Если вы этого еще не сделали, то пришла пора это сделать. После установки в вашей палитре компонентов появится новый компонент TOmniTaskEventDispatch. Этот компонент упростит создание связи между фоновым потоком и основным потоком программы. В будущем мы часто будем это использовать.
Приступим к дальнейшему разбору. Для этого загрузим приложение tests\1_HelloWorld библиотеки OTL. Как видите, разработчики позаботились о создании большого количества демонстрационных примеров, на которых мы будем разбирать в дальнейшем работу с OTL.
Итак, обратим внимание на процедуру btnHelloClick.
test_1_HelloWorld.pas   Создание потоковой задачи и связка с диспетчером мониторинга
procedure TfrmTestHelloWorld.btnHelloClick(Sender: TObject);

begin

  btnHello.Enabled := false;

  FMessageDispatch.Monitor(CreateTask(RunHelloWorld, 'HelloWorld')).Run;

end;

Данный код очень похож на код из предыдущего примера, но отличается одной важной модификацией – данный код передает интерфейс задачи, созданный в CreateTask, к компоненту TOmniTaskEventDispatch. Это даст возможность компоненту TOmniTaskEventDispatch  начать контролировать сообщения от созданного дополнительного потока.
Поскольку Вы можете видеть, что TOmniTaskEventDispatch.Monitor берет в качестве параметра интерфейс, возвращенный из CreateTask, а также возвращает тот же самый интерфейс как результат своей работы так, чтобы Вы могли вызвать метод Run для запуска созданной задачи в фоновом потоке.
Код также отключает кнопку. Кнопка будет включена снова, когда фоновый поток закончит свое выполнение.
Теперь мы разберем  код задачи, который будет выполняться в фоновом потоке.
test_1_HelloWorld.pas    Код выполняющийся в фоновом потоке, передача сообщений основному потоку программы
procedure TfrmTestOTL.RunHelloWorld(task: IOmniTask);

begin

  task.Comm.Send(0, 'Hello, world!');

end;

Библиотека OTL автоматически устанавливает двухсторонний канал между интерфейсом управления задачи (тот, который возвращен из CreateTask), и интерфейсом IOmniTask который передается рабочему методу, связанному с этой задачей. Оба интерфейса имеют свойство Comm,  который обеспечивает связь между этими интерфейсами.
Используя метод Send Вы можете отправлять сообщения следующими способами:
Для простых уведомительных сообщений (без данных) можно использовать метод Send с одним параметром, куда можно передать значение word или variant.
procedure Send(const msg: TOmniMessage); overload;
procedure Send(msgID: word); overload;
Для более сложных сообщений (сообщение, а также некие данные) можно воспользоваться парными сообщениями (msgID – идентификатор сообщения и msgData – передаваемые данные).
procedure Send(msgID: word; msgData: array of const); overload;
procedure Send(msgID: word; msgData: TOmniValue); overload;
В нашем примере, мы предаем парное сообщение с идентификатором сообщения равным 0 и данными в виде строки 'Hello, world!'.
Это все, что должно быть сделано в фоновой задаче (потоке) для того что бы уведомить основной поток программы о произошедшем событии с помощью сообщения.
На стороне GUI пользователя (в основном потоке программы) компонент OmniTaskEventDispatch1 уже контролирует поступающие сообщения от фоновой задачи. Для обработки их мы только должны написать обработчик OnTaskMessage.
test_1_HelloWorld.pas    Обработчик сообщений поступивших из фоновых задач
procedure TfrmTestOTL.OmniTaskEventDispatch1TaskMessage(task: IOmniTaskControl);

var

  msgID  : word;

  msgData: TOmniValue;

begin

  task.Comm.Receive(msgID, msgData);

  lbLog.ItemIndex := lbLog.Items.Add(Format('[%d/%s] %d|%s', [task.UniqueID,

                                                        task.Name, msgID, msgData]));

end;
Обработчик OnTaskMessage вызывается каждый раз, когда сообщение доступно. Данный код читает это сообщение (Receive) и показывает переданные данные в окне списка.
Также Вы можете увидеть, что каждой задаче назначается уникальный индентификатор UniqueID. Понажимайте кнопку несколько раз, и вы увидите, что связная задача получает новый уникальный идентификатор каждый раз, когда запускается на выполнение. Это очень важный момент, вы всегда сможете идентифицировать вашу фоновую задачу.
Помимо этого компонент OmniTaskEventDispatch1 также может отслеживать состояние фоновой задачи и с помощью соответствующих обработчиков уведомлять основной поток программы о запуске, окончании, прерывании фонового потока связанного с фоновой задачей.
Реализуется это написанием соответствующих обработчиков компонента TOmniEventMonitor.
В нашем упрощенном примере мы напишем, обработчик OnTaskTerminated, который будет сообщать нашему приложению о том, что фоновый поток  завершен. Конечно, для полноценного контроля за фоновой задачей нужно написать и дополнительные обработчики, но пока нам это не нужно.
test_1_HelloWorld.pas    Обработчик завершения фоновой задачи (окончание работы фонового потока)
procedure TfrmTestOTL.OmniTaskEventDispatch1TaskTerminated(task: IOmniTaskControl);

begin

  lbLog.ItemIndex := lbLog.Items.Add(Format('[%d/%s] Terminated', [task.UniqueID, task.Name]));

  btnHello.Enabled := true;

end;
Наконец-то мы разблокируем нашу кнопку.

Рис.Обработка сообщений от фоновой задачи (потока)

1 комментарий: