
Примечание
Метод Terminate автоматически вызывается и из деструктора объекта. Таким образом, если поток не умеет завершаться корректно, вызов деструктора потенциально может привести к зависанию всей программы.
Еще одно полезное свойство:
property FreeOnTerminate: Boolean;
Если это свойство равно True, то деструктор потока будет вызван автоматически по его завершении. Это очень удобно для тех случаев, когда нет уверенности в том, когда именно завершится поток, и необходимо использовать его по принципу "выстрелил и забыл" (fire and forget).
function WaitFor: Integer;
Метод WaitFor предназначен для синхронизации и позволяет одному потоку дождаться момента, когда завершится другой поток.
Если внутри потока FirstThread написать код:
Code := SecondThread.WaitFor;
то это означает, что поток FirstThread останавливается до момента завершения потока SecondThread.
Метод WaitFor возвращает код завершения ожидаемого потока (см. свойство ReturnValue).
property Handle: THandle read FHandle;
property ThreadID: THandle read FThreadID;
Свойства Handle и ThreadID дают программисту непосредственный доступ к потоку средствами API Win32. Если разработчик хочет обратиться к потоку и управлять им, минуя возможности класса TThread, значения Handle и ThreadID могут быть использованы в качестве аргументов функций API Win32.
Например, если программист хочет перед продолжением выполнения приложения дождаться завершения сразу нескольких потоков, он должен вызвать функцию API WaitForMultipieObjects; для ее вызова необходим массив дескрипторов потоков.
property Priority: TThreadPriority;
Свойство Priority позволяет запросить и установить приоритет потоков. Допустимыми значениями приоритета для объектов TThread являются tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest и tpTimeCritical.
procedure Synchronize (Method: TThreadMethod);
Этот метод относится к секции protected, т. е. может быть вызван только из потомков TThread.
Delphi предоставляет программисту метод Synchronize для безопасного вызова методов VCL внутри потоков.
Во избежание конфликтных ситуаций, метод Synchronize дает гарантию, что к каждому объекту VCL одновременно имеет доступ только один поток.
Аргумент, передаваемый в метод Synchronize, — это имя метода, который производит обращение к VCL; вызов Synchronize с этим параметром — это тоже, что и вызов самого метода. Такой метод (класса TThreadMethod) не должен иметь никаких параметров и не должен возвращать никаких значений. К примеру, в основной форме приложения нужно предусмотреть функцию:
procedure TMainForm.SyncShowMessage;
begin
ShowMessage (IntToStr (ThreadList1.Count)); // другие обращения к VCL
end;
а в потоке для показа сообщения писать не
ShowMessage (IntToStr(ThreadListl.Count));
и даже не
MainForm.SyncShowMessage;
а только так:
Synchronize (MainForm.SyncShowMessage);
Примечание
Производя любое обращение к объекту VCL из потока, убедитесь, что при этом используется метод Synchronize; в противном случае результаты могут оказаться непредсказуемыми. Это верно даже в том случае, если вы используете средства синхронизации, описанные ниже.
procedure Resume;
Метод Resume класса TThread вызывается, когда поток возобновляет выполнение после остановки, или для явного запуска потока, созданного с параметром CreateSuspended, равным True.
procedure Suspend;
Вызов метода Suspend приостанавливает поток с возможностью повторного запуска впоследствии. Метод Suspend приостанавливает поток вне зависимости от кода, исполняемого потоком в данный момент; выполнение продолжается с точки останова.
property Suspended: Boolean;
Свойство Suspended позволяет программисту определить, не приостановлен ли поток. С помощью этого свойства можно также запускать и останавливать поток.
Установив свойство Suspended в значение True, вы получите тот же результат, что и при вызове метода Suspend — приостановку. Наоборот, установка свойства Suspended в значение False возобновляет выполнение потока, как и вызов метода Resume.
property ReturnValue: Integer;
Свойство ReturnValue позволяет узнать и установить значение, возвращаемое потоком по его завершении. Эта величина полностью определяется пользователем. По умолчанию поток возвращает ноль, но если программист захочет вернуть другую величину, то простая переустановка свойства ReturnValue внутри потока позволит получить эту информацию другим потокам. Это, к примеру, может пригодиться, если внутри потока возникли проблемы, или с помощью свойства ReturnValue нужно вернуть число не прошедших орфографическую проверку слов.
Для иллюстрации приемов работы с потоком создадим программу, которая будет непрерывно обновлять содержимое многострочного редактора и при этом осуществлять математические вычисления.
Для ее создания сначала на форму поместим панель TPanel, очистим ее свойство Caption.
Свойству Align присвоим значение аlRight.
Эта панель предназначена для размещения редактора TSpinEdit, кнопки TButton и индикатора TGauge и всегда должна располагаться в правой части окна программы (компоненты TSpinEdit и TGauge находятся на странице Samples палитры компонентов).
Установим в свойство SpinEditl.Value начение 2,
присвоим свойству Gauge1.Kind значение gkPie,
Gaugel.BorderStyle – bsNone
и Button1.Caption — 'Квадрат'.
На свободное место формы положим компонент TMemo и установим для него в свойство Align значение alLeft, а свойство Name - 'mmOutput'.
Создадим обработчик события Button1.Click: при нажатии на кнопку вначале содержимое редактора SpinEdit1 возводится в квадрат до тех пор, пока отображаемое в нем значение не слишком велико (не больше 10 Е+1233).
В этот момент надпись на кнопке меняется на “корень”, а нажатие на нее вычисляет корень квадратный из величины SpinEdit1.
procedure TForm1.Button1Click(Sender: TObject) ;
begin
if Tag=0 then
begin
SpinEdit1.Text := FloatToStr (sqr (StrToFloat (SpinEditl.Text)));
if StrToFloat(SpinEditl.Text) > 1el233 then
begin
Tag := 1;
Button1.Caption:= 'Корень'
end
end
else
begin
SpinEdit1.Text:= FloatToStr(sqrt(StrToFloat(SpinEditl.Text))) ;
if StrToFloat (SpinEdit1.Text) < 2 then
begin
SpinEdit1.Value:= 2;
Tag := 0;
Button1.Caption := 'Квадрат'
end
end
end;
Таким образом, главный код программы связан с извлечением корня или возведением в квадрат величины, записанной в редакторе SpinEdit1.
Теперь создадим модуль потока, в методе Execute которого будем непрерывно формировать по 100 строк в редакторе mmOutput и показывать процент заполнения редактора с помощью индикатора Gauge1.
Выберем пиктограмму модуля потока в окне репозитория Delphi и дадим наследнику класса Thread имя ThreadDemo. Окончательный текст модуля потока может выглядеть так:
unit Unit2;
interface
uses
Classes;
type
ThreadDemo = class (TThread)
private
{ Private declarations }
protected
S : String;
N: Integer;
procedure UpdateMemo;
procedure UpdateGauge;
procedure Execute; override;
end;
var
TDemo: ThreadDemo;
implementation
uses Unit1, SysUtils;
Important: Methods and properties of objects in VCL can only reused .in a method called using Synchronize, for example,
Synchronize(UpdateCaption);
and UpdateCaption could look like,
procedure ThreadDemo.UpdateCaption;
begin
Formi.Caption := 'Updated in a thread';
end; }
ThreadDemo }
procedure ThreadDemo.Execute;
var
j, k: Integer;
begin
repeat
S:='';
Synchronize (UpdateMemo);
for k := 0 to 99 do
begin
N := k;
S : = ' ' ;
for j := 1 to 20 do
S := S+FormatFloat('00',k);
Synchronize (UpdateMemo);
Synchronize (UpdateGauge)
end;
until False
end;
Procedure ThreadDemo.UpdateMemo;
begin
with Form1.mmOutput.Lines do
if S=' ' then Clear else Add(S)
end;
Procedure ThreadDemo.UpdateGauge;
begin
Form1.Gauge1.Progress := N
end;
end.
Если вы запустите таким способом подготовленную программу, то ничего не произойдет – ведь поток еще не запущен. Чтобы сделать это, добавим в модуле Unit1 главной формы ссылку
uses Unit2,
Создадим обработчик события onActivate для Form1:
procedure TForm1.FormActivate(Sender: TObject);
begin
TDemo := ThreadDemo.Create (False),
end;
Вот так просто запускается дополнительный поток – инициируем объект TDemo, передавая в его Конструктор ThreadDemo.Create единственный параметр False (этот параметр показывает, должен ли вновь созданный поток “спать” – True или он обязан немедленно начать работу – False).
Программа в любой момент может приостановить работу потока, присвоив его свойству Suspended значение True, и продолжить его выполнение, присвоив этому свойству значение False.
Обратим внимание – метод Execute потока вынесен в секцию protected и поэтому недоступен из основного модуля. Выполнение этого метода начинается автоматически, как только свойство Suspended примет значение False.
Для обращения к свойствам и методам визуальных компонентов формы Form1 предназначен специальный метод потока Synchronize. Единственным параметром обращения к этому методу должно быть имя любой потоковой процедуры без параметров.
Внутри такой процедуры разрешается обращаться к методам и свойствам визуальных компонентов. В нашем потоке две такие процедуры – UрdateMemo и UpdateGuage. В первой строка S добавляется к содержимому редактора mmOutput, во втором – глобальная переменная n присваивается свойству progress индикатора Gauge1. Поскольку эти процедуры не могут иметь параметров, для управления их работой приходится использовать глобальные переменные S и N.
Delphi представляет программисту полный доступ к возможностям программирования интерфейса Win32 и при этом в Delphi имеется специальный класс для организации потоков. Вообще говоря, программист не обязан разбираться во всех тонкостях механизмов, предлагаемых операционной системой. Класс должен инкапсулировать и упрощать программный интерфейс; класс TThread — прекрасный пример предоставления разработчику простого доступа к программированию потоков.
Сам API потоков, вообще говоря, не очень сложен, но предоставленные классом TThread возможности вообще замечательно просты. В двух словах, все, что необходимо сделать, — это перекрыть виртуальный метод Execute.
Другая отличительная черта класса TThread — это гарантия безопасной работы с библиотекой визуальных компонентов VCL. Без использования класса TThread во время вызовов VCL могут возникнуть ситуации, требующие специальной синхронизации.
Нужно отдавать себе отчет, что с точки зрения операционной системы поток — это ее объект. При создании он получает дескриптор и отслеживается ОС. Объект класса TThread — это конструкция Delphi, соответствующая потоку ОС. Этот объект VCL создается до реального возникновения потока в системе и уничтожается после его исчезновения.