Главная » Delphi » Синхронизация с подпрограммами библиотеки  VCL

0

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

Преимущества однопоточного пользовательского интерфейса

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

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

Метод Synchronize()

В классе TThread определен метод Synchronize(), который позволяет вызывать некоторые из методов этого  класса прямо  из основного потока приложения. Опреде ление метода Synchronize() имеет следующий вид:

procedure Synchronize(Method: TThreadMethod);

Параметр Method имеет  тип  TThreadMethod (означающий процедурный метод, не использующий никаких параметров), который определяется следующим образом:

type

TThreadMethod = procedure of object;

Метод,  передаваемый в качестве параметра Method, и является как раз  тем мето дом,  который затем  выполняется из  основного потока приложения. Возвращаясь к примеру с классом  TTestThread, предположим, что  необходимо отобразить резуль тат вычислений в строке редактирования главной формы. Это можно  сделать  введя в класс TTestThread метод,  который вносит необходимые изменения в свойство Text строки редактирования, и осуществив последующий вызов данного метода с помощью процедуры Synchronize().

Предположим, что этот метод называется GiveAnswer(). В листинге 5.1 представ 

лен полный исходный код модуля ThrdU, который включает программную реализацию

процесса обновления упомянутой выше строки редактирования в главной форме.

Листинг 5.1. Модуль ThrdU.PAS

unit ThrdU;

interface uses

Classes;

type

TTestThread = class(TThread)

private

Answer: integer;

protected

procedure GiveAnswer;

procedure Execute; override;

end;

implementation

uses SysUtils, Main;

{ TTestThread }

procedure TTestThread.GiveAnswer;

begin

MainForm.Edit1.Text := InttoStr(Answer);end;

procedure TTestThread.Execute;

var

I: Integer;

begin

FreeOnTerminate := True;

for I := 1 to         2000000 do

begin

if Terminated then Break;

Inc(Answer, Round(Abs(Sin(Sqrt(I)))));

Synchronize(GiveAnswer);

end;

end;

end.Как уже было сказано, с помощью метода  Synchronize() можно  выполнять мето ды в контексте основного потока, но до сих пор  речь  о них шла как о таинственном “черном ящике”.  Было  известно, что он делает,  но неизвестно как. Настало время приоткрыть завесу этой тайны.

При  первом создании вторичного (или  дополнительного) потока в приложении библиотека VCL создает  и далее поддерживает скрытое окно потока (thread window) в контексте своего  основного потока. Единственная цель этого  окна  заключается в ор ганизации последовательности вызовов процедур, выполненных с помощью метода Synchronize().

Метод Synchronize() сохраняет метод,  заданный параметром Method, в закрытом

поле  FMethod и посылает определенное библиотекой VCL сообщение CM_EXECPROC в

окно  потока, передавая в качестве параметра lParam данного сообщения  значение

Self (в этом случае Self указывает на объект TThread). Когда процедура окна потока

получает сообщение CM_EXECPROC, она  вызывает метод,  заданный в поле  FMethod с

помощью экземпляра объекта TThread, переданного параметром lParam. Напомним,

что,  поскольку  это  окно  было  создано  в контексте основного потока, процедура окна

для него также выполняется основным потоком. Следовательно, метод, указанный в по

ле FMethod, также вызывается основным потоком.

Графическая иллюстрация того,  что  происходит внутри  метода  Synchronize(),представлена на рис. 5.2.

Рис. 5.2. Схема работы метода Synchronize()

Использование сообщений для синхронизации

В  качестве  альтернативы  методу   TThread.Synchronize() существует   другой способ  синхронизации, который заключается в использовании сообщений, переда ваемых  между потоками. В этом случае для отправки сообщений в окна,  действующие в контексте другого  потока, используется функция SendMessage() или  PostMes- sage(). Например, приведенный ниже  код можно  было  бы  применить для  вывода текста в строке редактирования, расположенной в другом потоке:

var

S: string;

begin

S := ‘hello from threadland';

SendMessage(SomeEdit.Handle, WM_SETTEXT, 0, Integer(PChar(S)));

end;

Источник: Тейксейра, Стив, Пачеко, Ксавье.   Borland Delphi 6. Руководство разработчика. : Пер.  с англ. — М. : Издательский дом “Вильямс”, 2002. —  1120 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии