Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебник Емельянов.doc
Скачиваний:
12
Добавлен:
03.11.2018
Размер:
3.25 Mб
Скачать

Класс tthread

Для реализации потоков Delphi предоставляет класс TThread. Наиболее важными методами являются Execute и Synchronize. Первый метод осу­ществляет выполнение потока. Этот метод абстрактный. Поэтому необходи­мо создать класс, который является потомком TThread, и в этом классе пере­определить метод Execute. Второй метод Synchronize, чтобы избежать одно­временного использования компонентов приложения, сообщает основной подзадаче, что требуется обращение к ресурсам, которые, возможно, заняты. При этом передается адрес метода, который основная подзадача может вы­звать, чтобы выполнить требуемую потоком работу. Процесс извещения яв­ляется последовательным и основная подзадача должна полностью завер­шить выполняемую работу, прежде чем получить следующее извещение. Извещающее сообщение отсылается при помощи подпрограммы Windows SendMessage. Результатом является то, что подзадача будет заморожена вплоть до полной отработки задания внутри основной подзадачи.

В программе перед выполнением потока, являющегося классом, естест­венно, должен быть построен соответствующий объект. Для этого вызывает­ся метод create, имеющий один аргумент CreateSuspended типа Boolean. Если поток создан со значением аргумента CreateSuspended = false, то он немедленно (как только получит время процессора в соответствии с его собственным приоритетом и приоритетом процесса) начинает выполняться (запускается метод Execute), в противном случае Execute запускается вы-

214

зовом метода Resume. Внутри метода Execute могут выполняться некото­рые циклические действия, для завершения которых можно воспользоваться свойством Terminated. Устанавливая значение этого свойства равным true внутри метода Execute или извне, поток получает команду о завершении его работы. С помощью метода Suspend можно приостановить работу пото­ка и затем продолжить, вызывая метод Resume. Переустанавливая значение свойства Priority, можно управлять приоритетом потока.

ПРИМЕР ПРИЛОЖЕНИЯ 27

Рассмотрим простой пример по использованию одного дополнительного потока. Пусть некоторый поток с помощью собственного метода, например, Paint применяется для закраски некоторой области прямоугольной формы. Основной поток приложения создается для рисования окружностей внутри той же области с помощью обработчика OnMouseDown.

Для построения приложения необходимо создать проект с двумя моду­лями. Один модуль - основная программа с объявленной переменной типа поток в секции Public внутри создавемого класса формы, другой модуль -модуль потока. Поместим на форму две кнопки TButton. Создадим для них два обработчика OnClick. Для формы, как уже отмечено, создается обработ­чик OnMouseDown. Далее создадим модуль для реализации вспомогательно­го потока (основной поток автоматически создается для процесса (приложе­ния)).

Необходимо открыть меню File - New и выбрать пункт Other. Откроется диалоговое окно, в котором нужно выбрать (дважды щелкнуть мышкой) значок Thread Object. Когда появится диалоговое окно, необходимо ввести имя класса TThread (Class Name = TPaintThread) и нажать на кнопку ОК. Ниже приводится весь программный код данного примера, который демон­стрирует использование метода Synchronize.

unit MyThreadl; {Модуль для определения потока}

interface

uses Classes;

type

TPaintThread = class(TThread)

private

x,у:integer;

protected

procedure Execute; override; procedure Paint;

end; implementation

uses Unit27_1, Graphics; procedure TPaintThread.Execute; begin

215

randomize; Repeat

x:=random(250);

y:=random(Form1.ClientHeight); Synchronize(Faint) ;

until Terminated; end;

procedure TPaintThread.Paint; begin

Form1.Canvas.Pixels[x,y]:=clPurple; end; end.

SysUtils, Variants, Classes, Forms, Dialogs, StdCtrls, ExtCtrls,

unit Unit27_1; {Основной модуль} interface

uses Windows, Messages, Graphics, Controls, Buttons, MyThreadl ;

type

TForm1 = class(TForm) Button1: TButton; Button2: TButton; BitBtnl: TBitBtn;

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

procedure FormMouseDown(Sender: TObject; Button:

TMouseButton; Shift: TShiftState; X, Y: Integer);

public

MyPaintThread:TPaintThread;

end;

var Form1: TForm1; implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject); begin

Button1,Enabled:=false;

Button2.Enabled:=true;

MyPaintThread:=TPaintThread.Create(false);

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

MyPaintThread.Free;

Button1.Enabled := True;

Button2.Enabled := False; end; procedure TForm1.FormMouseDown(Sender: TObject;

Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin

Canvas.Pen.Color := Color;

216

Canvas.Brush.Color := Color;

Canvas.Ellipse (x - 30, у - 30, x + 30, у + 30);

end; end.

Следует обратить внимание на строку MyPaintThread:TPaintThread; и использование модуля Graphics. Среда Delphi может не добавить этот модуль в список Dses. На рис. 63 приводится вариант решения данного примера.

Рис. 63

ИСПОЛЬЗОВАНИЕ БЛОКИРОВКИ В ПРИМЕРЕ 27

Можно написать другой вариант решения данного примера, воспользо­вавшись особенностью компонента TCanvas. Дело в том, что этот компо­нент содержит два метода Lock и UnLock, которые позволяют блокировать вывод на канву с последующей разблокировкой. Естественно, при использо­вании этих методов канвы отпадает необходимость синхронизации работы потоков с помощью метода Synchronize.

Код программы приводится далее. Из текста кода основного модуля приводится только один обработчик TForm1. FormMouseDown, в который внесены изменения. Отметим, что код потока немного упростился.

unit MyTread2; interface

uses Classes; type

217

TPaintThread = class(TThread)

protected

procedure Execute; override;

end; implementation

uses Unit27_2, Graphics; procedure TPaintThread.Execute;

var x,у:integer; begin

randomize; Repeat

x:=random(250);

y:=random(Form1.ClientHeight); with Form1.Canvas do begin

Lock;

try

Pixels[x,y]:=clPurple; finally

UnLock;

end; end;

until Terminated; end;

end.

unit Unit27_2;

…………………….

procedure TForm1.FormMouseDown(Sender: TObject;

Button: TMouseButton; Shift: TShiftState; X, Y: Integer), begin

Canvas.Lock; try

Canvas.Pen.Color := Color; Canvas.Brush.Color := Color;

Canvas.Ellipse (x - 30, у - 30, x + 30, у + 30); finally

Canvas.Unlock;

end;

end;

end.

Нельзя не заметить, что второй вариант примера работает значительно быстрее. Но, к сожалению, не для любых потоков можно применить подоб­ные приемы блокировки.

МНОГОПОТОЧНОЕ ПРИЛОЖЕНИЕ В ПРИМЕРЕ 28

В данном примере создаются три потока, которые закрашивают разными цветами один и тот же прямоугольник, как и в примере 27. Однако способ

218

закраски в примере 28 отличается тем, что используется сканирование пря­моугольной области. В варианте, который приводится ниже, используется встроенная в класс TThread система безопасности с помощью метода син­хронизации. Следует отметить, что данный подход не спасает приложение и компьютер от зависания, поэтому этот вариант примера лучше запускать из среды Delphi, чтобы можно было воспользоваться клавишами Ctrl+F2. Правда, вероятность зависания зависит от установленных приоритетов для каждого потока.

В примере используются новые компоненты: TTrackBar - для измене­ния приоритета потоков и TProgressBar - для визуального контроля скоро­сти работы того или иного потока. Компонент TTrackBar применяется, ко­гда требуется изменять значения в заданном диапазоне. Текущее значение определяется свойством Position, диапазон значений задается свойствами Min и Мах. В примере задано Min=0, Max=3. Второй компонент (TPro­gressBar на странице Win32 - индикатор хода работ) показывает графиче­скую полосу хода выполнения какой-либо операции. Текущая позиция ин­дикатора определяется свойством Position, свойства Min и Мах задают диа­пазон индикатора. В примере задано Min=0, Max определяется свойством формы ClientHeight, т.е. в данном случае числом дисплейных строк. Число участков, на которое делится весь диапазон, задано равным 10. Это число определяет величину шага сканирования диапазона, которое записывается в свойстве Step. Ниже приводится программный код примера. Отметим, что в обработчике TrackBar1Change используются свойства Tag компонентов TtrackBar, которые соответственно необходимо с помощью инспектора объектов установить равными 1, 2, 3.

unit MyThread3;

interface

uses Classes, Graphics, ComCtrls;

type

TPaintThread = class(TThread) private

x,у:integer; FColor:TColor; FProgressBar:TProgressBar; public

constructor InitColor(nColor:TColor);

property ProgressBar:TProgressBar write FProgressBar; procedure DisplayProgress; protected

procedure Execute; override; procedure Paint; end;

implementation uses Unit28;

219

constructor TPaintThread.InitColor(nColorrTColor); begin

Fcolor:=nColor;

end;

procedure TPaintThread.DisplayProgress;

begin

FprogressBar.Position:=y;

end;

procedure TPaintThread.Paint;

var j: Integer;

begin

for j:=0 to 250 do begin x: = j ;

Form1.Canvas.Pixels[x,y]:=FColor;

end;

end;

procedure TPaintThread.Execute;

var i: Integer;

begin

repeat

for i:=0 to Form1.ClientHeight do begin

y:=i;

Synchronize(DisplayProgress);

Synchronize(Paint);

end;

until Terminated;

end;

end.

unit Unit28;

interface uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, ComCtrls, MyThread3;

Type TForm1 = class(TForm)

TrackBar1: TTrackBar;

TrackBar2: TTrackBar;

ТгаскВагЗ: TTrackBar;

BitBtnl: TBitBtn;

CheckBox1: TCheckBox;

CheckBox2: TCheckBox;

CheckBox3: TCheckBox;

ProgressBar1: TProgressBar;

ProgressBar2: TProgressBar;

ProgressBar3: TProgressBar;

procedure CheckBox1Click(Sender: TObject);

procedure TrackBar1Change(Sender: TObject);

220

procedure FormCreate(Sender: TObject);

procedure FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); public

PT: array [1..3] of TPaintThread;

end;

var Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.CheckBox1Click(Sender: TObject);

begin

Form1.Canvas.Lock;

if (Sender as TCheckbox).Checked then

PT [(Sender as TCheckbox).Tag].Resume

else PT [(Sender as TCheckbox).Tag].Suspend;

Form1.Canvas.UnLock;

end;

procedure TForm1.TrackBar1Change(Sender: TObject);

begin

PT [(Sender as TTrackBar).Tag].Priority :=

TThreadPriority ((Sender as TTrackBar).Position);

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

PT [1] := TPaintThread.Create (true); PT [2] := TPaintThread.Create (true); PT [3] := TPaintThread.Create (true);

PT[1].InitColor(clRed);

PT[2].InitColor(clBlue);

PT[3].InitColor(clGreen);

ProgressBar1.Max:=Form1.ClientHeight;

ProgressBar2.Max:=Form1.ClientHeight;

ProgressBar3.Max:=Form1.ClientHeight;

ProgressBar1.Step:=Form1.ClientHeight div 10;

ProgressBar2.Step:=Form1.ClientHeight div 10;

ProgressBar3.Step:=Form1.ClientHeight div 10;

PT[1].ProgressBar:=ProgressBar1;

PT[2].ProgressBar:=ProgressBar2;

PT[3].ProgressBar:=ProgressBar3 ;

end;

procedure TForm1.FormMouseDown(Sender: TObj ect;

Button: TMouseButton;

Shift: TShiftState; X, Y: Integer); begin Canvas.Lock;

try

Canvas.Pen.Color := Color;

Canvas.Brush.Color := Color;

221

Canvas.Ellipse (x - 30, у - 30, x + 30, у + 30); finally

Canvas.Unlock;

end; end; end.

Результат решения примера приводится на рис. 64 (показаны установки приоритетов, обеспечивающие достаточно надежную работу приложения).

Рис. 64

В примере создается массив рт из трех потоков. Цвет закраски прямо­угольной области задается с помощью вызова конструктора initcolor. Разделение общих ресурсов, используемых потоками, обеспечивается мето­дом Synchronize. Для ускорения работы синхронизируется работа не каж­дого доступа к канве по изменению цвета пиксела (Pixels [x,y]), а только очередной дисплейной строки. Можно попытаться синхронизировать доступ к каждому пикселу, возможно, это обеспечит более надежную работу (но замедлится работа приложения). Надежной работы данной программы мож­но добиться, используя методы блокировки канвы, как это было применено в примере 27. Далее для этого случая приводится код модуля, в котором объ­явлен поток (код основного модуля не изменился по сравнению с предыду­щим примером).

unit MyThread3_1;

interface

uses Classes, Graphics, ComCtrls;

222

type

TPaintThread = class(TThread)

private FColor:TColor; FProgressBar:TProgressBar;

public

constructor InitColor(nColor:TColor);

property ProgressBar:TProgressBar write FProgressBar; protected

procedure Execute; override; end;

implementation

uses Unit28_1;

constructor TPaintThread.InitColor(nColor:TColor);

begin

Fcolor:=nColor;

end;

procedure TPaintThread.Execute;

var x,y: Integer; begin

repeat

for y:=0 to Form1.ClientHeight do begin FprogressBar.Position:=y; Form1.Canvas.Lock;

try

for x:=0 to 250 do

Form1.Canvas.Pixels[x,y]:=FColor; finally

Form1.Canvas.UnLock; end; end;

until Terminated; end;

end.

Единственное, что можно отметить для данного варианта, это то, что на­блюдается некоторое притормаживание, когда включается в работу основ­ной поток - рисование круга (или происходит нажатие на кнопку выхода из приложения). Это связано с тем, что блокируется не одна операция Pix­els [х,у], а весь цикл закраски пикселов в данной строке.