Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование лекции.doc
Скачиваний:
49
Добавлен:
12.11.2019
Размер:
5.53 Mб
Скачать

22.1. Создание многопоточных приложений

Для организации параллельных процессов в Delphi предусмотрен особый тип данных Thread (поток). Название "поток" в данном случае не имеет ничего общего с файловым потоком (см. п. 17.5на с. 134). Надо отметить, что работа с потоками достаточно сложна и неочевидна.

Создадим программу, выполняющую простейшее действие – сложение двух введенных чисел. Создаем новое приложение и сохраняем файл программы под именем unit1.pas (это важно!), а файл проекта – project1.dpr. Поместим на форму два компонента типа Tedit, кнопку и компонент Tlabel. В обработчике нажатия на кнопку напишем очевидный код:

procedure TForm1.Button1Click(Sender: TObject);

var a,b:real;

begin

TRY

a:=StrToFloat(Edit1.Text);

b:=StrToFloat(Edit2.Text)

EXCEPT

MessageDlg('Неверные данные', mtError,[mbOK],0);

EXIT

END;

Label2.Caption:=FloatToStr(a+b)

end;

А теперь заставим цвет формы плавно меняться, перебирая все возможные оттенки. Цвет формируется значениями интенсивности красного, синего и зеленого. Интенсивность каждого цвета хранится в одном байте и соответственно принимает значения от 0 до 255. Поэтому для перебора всех цветов можно использовать три вложенных цикла:

for red:=0 to 255 do

for green:=0 to 255 do

for blue:=0 to 255 do

begin

form1.Color:=RGB(Red,Green,Blue);

application.ProcessMessages

end;

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

Для создания потока выполним команду меню FileNewOtherThread Object ( ). В появившемся окошке следует ввести произвольное имя потока, к примеру, Time. Delphi автоматически создаст файл-заготовку для потока с описанием вида

type

Time = class(TThread)

private

{ Private declarations }

protected

procedure Execute; override;

end;

Сразу сохраним файл-заготовку под именем unit2.pas. Поток имеет процедуру Execute, внутри которой и вызывается код, исполняемый в потоке. Однако из процедуры Execute нельзя напрямую обращаться к компонентам. Это связано с тем, что в многопоточном приложении несколько потоков могут одновременно захотеть изменить одно и то же свойство одного и того же компонента. Скажем, при отрисовке, один и тот же пиксел один поток требует окрасить в черный цвет, а другой – в белый. Для исключения подобных коллизий необходимо применять так называемую синхронизацию потоков. Синхронизация обеспечивает последовательный доступ потоков к свойствам компонентов и позволяет избежать конфликтов.

Для синхронизации, прежде всего надо весь код, изменяющий свойства компонентов, вынести в отдельную процедуру. Чтобы в модуле unit2.pas можно было "видеть" форму, описанную в модуле unit1.pas и, наоборот, в операторы USES надо добавить ссылки модулей друг на друга. В файле unit1.pas в самом начале в конец списка модулей в операторе USES добавим unit2. А вот в файле unit2.pas так сделать не удастся – Delphi запрещает ситуацию, когда два модуля ссылаются друг на друга (так называемая "круговая ссылка"). Что же делать? Выход очень простой: ввести ссылку в раздел Implementation:

Implementation

uses unit1, Windows, Forms;

Как видно из примера, приходится вручную прописывать и ссылки на библиотечные модули Windows (в нем хранится функция RGB) и Forms (в нем хранится переменная Application).

Цикл смены цветов будет выполняться бесконечно, но надо предусмотреть возможность его останова при завершении работы программы. В модуле unit1 объявим глобальную переменную br:

var

Form1: TForm1;

Thread: Time;

br:BOOLEAN=false;

Здесь же объявим и переменную Thread, которая будет представлять дочерний поток в программе.

Переменная br должна устанавливаться в True при запросе пользователя на закрытие формы. Для этого создаем обработчик события формы OnCloseQuery:

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);

begin

br:=true

end;

В модуле unit2 в класс Time добавляем заголовок нашей будущей процедуры:

type

Time = class(TThread)

private

{ Private declarations }

procedure Show; { эту строчку дописать }

protected

procedure Execute; override;

end;

Далее пишем саму процедуру Show:

procedure Time.Show;

var red, green, blue:byte;

begin

for red:=0 to 255 do

for green:=0 to 255 do

for blue:=0 to 255 do

begin

form1.Color:=RGB(Red,Green,Blue);

application.ProcessMessages;

if br then exit

end

end;

Оператор If обеспечивает выход из процедуры при закрытии программы. Осталось организовать синхронный вызов процедуры Show. В заготовке процедуры Execute пишем:

procedure Time.Execute;

begin

{ Place thread code here }

repeat

Synchronize(Show)

until terminated;

end;

Здесь цикл Repeat..Until вызывает метод Synchronize, который, в свою очередь, синхронно и корректно выполняет нашу процедуру Show. Цикл прекращается при завершении потока, когда его свойство Terminated устанавливается в True.

В модуле unit1 осталось организовать запуск потока и прекращение его работы. Пишем обработчик события создания формы OnCreate:

procedure TForm1.FormCreate(Sender: TObject);

begin

DoubleBuffered:=TRUE;

Thread:=Time.Create(false);

Thread.FreeOnTerminate:=true;

Thread.Priority:=tpLower

end;

Поток создается вызовом метода Create. Параметр False указывает на то, что поток начнет исполняться немедленно после создания. Свойство FreeOnTerminate, будучи установленным в True, автоматически освобождает занятую потоком память после завершения его работы. Свойство Priority указывает, какая доля процессорного времени отводится на выполнение потока. Возможны следующие варианты:

Табл. 22.2

Константа

Описание

tpIdle

Поток выполняется, только если приложение находится в режиме ожидания и ничего не делает

tpLowest

Приоритет на два уровня ниже нормального

tpLower

Приоритет на один уровень ниже нормального

tpNormal

Нормальный приоритет

tpHigher

Приоритет на один уровень выше нормального

tpHighest

Приоритет на два уровня выше нормального

tpTimeCritical

Наивысший приоритет

Установка приоритета прямо влияет на скорость выполнения потока. При установке высоких приоритетов поток начинает "тормозить" как основную программу, так и всю операционную систему.

При завершении работы программы необходимо завершить и поток. В обработчик события OnCLoseQuery допишем строчку Thread.Terminate:

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);

begin

br:=true;

Thread.Terminate

end;

Готово! Запускаем программу и видим, как непрерывно меняется фон формы, не мешая при этом выполнять вычисления.