Основные приоритеты (базовые), устанавливаемые для процессов
Класс приоритета | Наименование | Основной приоритет |
IDLE_PRIORITY_CLASS | Пассивный (фоновый) | |
NORMAL_PRIORITY_CLASS | Нормальный | 7 (фоновый) или 9 (переднего плана) |
HIGH_PRIORITY_CLASS | Высокий | |
REALTIME_PRIORITY_CLASS | Реального времени |
Существуют свои приоритеты и для потоков, но не абсолютные, а относительные – зависящие от приоритета порождающего их процесса. Таким образом при установке приоритета для потока необходимо сообщить системе, более высокий или низкий приоритет по сравнению с базовым следует установить, и система справится с этим сама. Естественно, чтобы выбрать для потока приоритет TIME_CRITICAL, нужно иметь очень веские основания, иначе другие процессы в системе могут не получить достаточного процессорного времени.
КЛАСС TTHREAD
Для реализации потоков Delphi предоставляет класс TThread.Наиболее важными методами являются Execute и Synchronize. Первый метод осуществляет выполнение потока. Этот метод абстрактный. Поэтому необходимо создать класс, который является потомком TThread, и в этом классе переопределить метод Execute. Второй метод Synchronize, чтобы избежать одновременного использования компонентов приложения, сообщает основной подзадаче, что требуется обращение к ресурсам, которые, возможно, заняты. При этом передается адрес метода, который основная подзадача может вызвать, чтобы выполнить требуемую потоком работу. Процесс извещения является последовательным и основная подзадача должна полностью завершить выполняемую работу, прежде чем получить следующее извещение. Извещающее сообщение отсылается при помощи подпрограммы Windows SendMessage. Результатом является то, что подзадача будет заморожена вплоть до полной отработки задания внутри основной подзадачи.
В программе перед выполнением потока, являющегося классом, естественно, должен быть построен соответствующий объект. Для этого вызывается метод Create, имеющий один аргумент CreateSuspended типа Boolean. Если поток создан со значением аргумента CreateSuspended = false, то он немедленно (как только получит время процессора в соответствии с его собственным приоритетом и приоритетом процесса) начинает выполняться (запускается метод Execute), в противном случаеExecuteзапускается вызовом метода Resume. Внутри метода Executeмогут выполняться некоторые циклические действия, для завершения которых можно воспользоваться свойством Terminated. Устанавливая значение этого свойства равным true или внутри метода Execute или извне, поток получает команду о завершении его работы. С помощью метода Suspend можно приостановить работу потока и затем продолжить, вызывая метод Resume. Переустанавливая значение свойства Priority, можно управлять приоритетом потока.
ПРИМЕР 27
Рассмотрим простой пример по использованию одного дополнительного потока. Пусть некоторый поток с помощью собственного метода, например, Paint применяется для закраски некоторой области прямоугольной формы. Основной поток приложения создается для рисования окружностей внутри той же области с помощью обработчика OnMouseDown.
Для построения приложения необходимо создать проект с двумя модулями. Один модуль – основная программа с объявленной переменной типа поток в секции Public внутри создавемого класса формы, другой модуль – модуль потока. Поместим на форму две кнопки TButton. Создадим для них два обработчика OnClick. Для формы, как уже отмечено, создается обработчик OnMouseDown. Далее создадим модуль для реализации вспомогательного потока (основной поток автоматически создается для процесса (приложения)).
Необходимо открыть меню File - New и выбрать пункт Other. Откроется диалоговое окно, в котором нужно выбрать (дважды щелкнуть мышкой) значок Thread Object. Когда появится диалоговое окно для именования объекта поток, необходимо ввести имя класса TThread (Class Name = TPaintThread) и нажать на кнопку OK. Ниже приводится весь программный код данного примера, который демонстрирует использование метода Synchronize.
unit MyThread1; {Модуль для определения потока}
Interface
uses Classes;
Type
TPaintThread = class(TThread)
Private
x,y:integer;
Protected
procedure Execute; override;
procedure Paint;
end;
Implementation
uses Unit27_1, Graphics;
procedure TPaintThread.Execute;
begin
randomize;
Repeat
x:=random(250);
y:=random(Form1.ClientHeight);
Synchronize(Paint);
until Terminated;
end;
procedure TPaintThread.Paint;
begin
Form1.Canvas.Pixels[x,y]:=clPurple;
end;
End.
unit Unit27_1; {Основной модуль}
Interface
Uses
Windows, Messages, SysUtils, Variants, Classes,
Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, Buttons, MyThread1;
Type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
BitBtn1: 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;
Canvas.Brush.Color := Color;
Canvas.Ellipse (x - 30, y - 30, x + 30, y + 30);
end;
End.
Следует обратить внимание на строку MyPaintThread:TPaintThread;и использование модуля Graphics. Среда Delphi может не добавить этот модуль в список Uses. На рис. 63 приводится вариант решения данного примера.
Рис. 63. Вариант решения примера 27.
ИСПОЛЬЗОВАНИЕ БЛОКИРОВКИ В ПРИМЕРЕ 27
Можно написать другой вариант решения данного примера, воспользовавшись особенностью компонента TCanvas. Дело в том, что этот компонент содержит два метода Lock и UnLock, которые позволяют блокировать вывод на канву с последующей разблокировкой. Естественно, при использовании этих методов канвы отпадает необходимость синхронизации работы потоков с помощью метода Synchronize.
Код программы приводится далее. Из текста кода основного модуля приводится только один обработчикTForm1.FormMouseDown, в который внесены изменения. Отметим, что код потока немного упростился.
unit MyTread2;
Interface
uses Classes;
Type
TPaintThread = class(TThread)
protected
procedure Execute; override;
end;
Implementation
uses Unit27_2, Graphics;
procedure TPaintThread.Execute;
var x,y: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, y - 30, x + 30, y + 30);
finally
Canvas.Unlock;
end;
end;
End.
Нельзя не заметить, что второй вариант примера работает значительно быстрее. Но, к сожалению, не все компоненты содержат указанные выше методы Lock и Unlock, поэтому не для любых потоков можно применить подобные приемы блокировки.
МНОГОПОТОЧНОЕ ПРИЛОЖЕНИЕ В ПРИМЕРЕ 28
В данном примере создаются три потока, которые закрашивают разным цветом один и тот же прямоугольник, как и в примере 27. Однако способ закраски в примере 28 отличается тем, что используется сканирование прямоугольной области. В варианте, который приводится ниже используется встроенная в класс TThread система безопасности с помощью метода синхронизации. Следует отметить, что данный подход не спасает приложение и компьютер от зависания, поэтому этот вариант примера лучше запускать из среды Delphi, чтобы можно было воспользоваться клавишами Ctrl+F2. Правда, вероятность зависания зависит от установленных приоритетов для каждого потока.
В примере используются новые компоненты: TTrackBar - для изменения приоритета потоков и TProgressBar – для визуального контроля скорости работы того или иного потока. Компонент TTrackBar применяется, когда требуется изменять значения в заданном диапазоне. Текущее значение определяется свойством Position, диапазон значений задается свойствами Min и Max. В примере задано Min=0, Max=3. Второй компонент (TProgressBar на странице Win32 - индикатор хода работ) показывает графическую полосу хода выполнения какой-либо операции. Текущая позиция индикатора определяется свойством Position, свойства Min и Max задают диапазон индикатора. В примере задано 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,y:integer;
FColor:TColor;
FProgressBar:TProgressBar;
public
constructor InitColor(nColor:TColor);