Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЛР Методы программирования Build1.0.pdf
Скачиваний:
98
Добавлен:
10.06.2015
Размер:
1.89 Mб
Скачать

91

Приложение Д. Элементарный поток – класс TThread

Для программной реализации потока в Delphi описан специальный класс TThread. Так как класс абстрактен, то в первозданном виде он нам никогда не понадобиться. Но созданные на его основе объекты превосходно инкапсулируют полнофункциональные потоки.

На палитре компонентов Delphi элемент управления TThread вы не найдёте. Для того, чтобы самое обычное приложение сделать многопоточным требуется к уже существующему приложению добавить специальный программный модуль. Для этого, после создания нового приложения выберите пункт меню File – New – Other. И на вкладке “New” диалогового окна “New Items" найдите пиктограмму ThreadObject. После нажатия кнопки OK в новом диалоговом окне требуется ввести имя нового класса – потока. Например: TMyThread. В результате выполненных действий Delphi создаёт представленный ниже шаблон, предназначенный для описания потока. Обратите внимание, что в секции Protected среда Delphi уже подготовила перекрытую процедуру Execute(). Именно в теле этой процедуры программист располагает программный код, который станет исполняться в отдельном потоке. Если подзадача вызывается многократно, то строки кода помещаются внутрь тела цикла, выход из которого завершает работу потока.

Рисунок – Создание многопоточной программы

unit Unit2;

interface

uses Classes; type

TMyThread = class(TThread) //класс TMyThread создаётся на базе абстрактного TThread private

{ Private declarations } protected

procedure Execute; override; end;

implementation

procedure TMyThread.Execute; begin

{место для исходного кода обслуживающего поток} end;

end.

92

Для запуска потока вызывайте метод:

procedure Execute;

Говоря образно, эта процедура заставит биться сердце потока.

При старте приложения автоматического создания экземпляра класса TMyThread не произойдёт. Для рождения нового потока Delphi потребует явного обращения к его конструктору:

constructor Create(CreateSuspended : Boolean);

Единственный параметр конструктора определяет, каким образом производится запуск потока. Если параметр CreateSuspended установлен в False, то поток стартует немедленно сразу после создания экземпляра класса. Если же передаётся значение true, то запуск потока откладывается до его явного вызова из какой-нибудь части кода программы.

var Thread: TMyThread;

procedure TfrmMain.FormCreate(Sender: TObject); begin

MyThread:=TMyThread.Create(true); end;

Для временной приостановки выполнения потока используйте процедуру:

procedure Suspend;

Для возобновления работы приостановленного потока:

procedure Resume;

Причём возобновление выполнения потока произойдёт именно с того места (с той строки кода), где он был приостановлен. Два названных метода дублируются свойством:

property Suspended : Boolean;

Если Вы установите свойство в true, то поток приостановится, false – вновь активизируется.

Для временной приостановки потока программисты Delphi часто пользуются процедурой Win32 API Sleep(). В качестве параметра передаётся число миллисекунд, на которые необходимо заморозить подзадачу.

procedure Sleep(milliseconds: Cardinal);

Поток, переведённый в состояние сна с помощью этого метода, не снижает производительность системы, так как не требует выделения ему квантов времени.

Для полной остановки потока вызывайте метод:

procedure Terminate;

Это способ остановки называется мягким. Процедура просто-напросто присваивает значение true свойству:

property Terminated: Boolean;

В этом случае Delphi не настаивает, на немедленном прекращении работы потока и разрешает ему достичь логического конца. Поясняю это утверждение на примере. Обычно исполняемый потоком код заключается в рамки цикла while..do или repeat..until, условием выхода из которого будет переход свойства Terminated в состояние true.

Procedure TMyThread.Execute;

Begin

While Terminated=false do //условие продолжения цикла

Begin

{исполняемый код}

End; end;

93

Как видите, если в момент вызова метода Terminated() выполнялся код расположенный внутри конструкции begin..end, то он не будет немедленно прерван. Останов произойдёт только при проверке условия цикла. Если есть мягкий способ остановки потока, то где-то в недрах Windows можно обнаружить и “жёсткий”. Если вы сторонник радикальных решений и категорически настаиваете на том, чтобы поток завершился немедленно, то пригодится функция из арсенала Windows API.

Function TerminateThread(hThread : THandle; ExitCode : Cardinal): boolean;

Здесь hThread – дескриптор потока, ExitCode – программный код выхода из потока, в простейшем случае передавайте значение 0. Если функция завершается успешно, то она вернёт не нулевое значение. Дескриптор потока вы найдёте в свойстве:

property Handle: THandle;

TerminateThread(Thread1.Handle, 0);

Останов потока сопровождается вызовом обработчика события:

property OnTerminate: TNotifyEvent;

Если установить в true свойство:

property FreeOnTerminate: Boolean;

то сразу после останова поток уничтожится. Иначе за вызов деструктора потока отвечаете Вы. Если выполнение метода Execute() прервалось в результате ошибки, и ошибка не была обработана в рамках этого метода, то в свойство только для чтения:

property FatalException: TObject; //только для чтения

будет записан объект ошибки. Это свойство, по крайней мере, можно применять для проверки корректности завершения метода Execute().

Ещё одним способом, косвенно проверяющим корректность завершения работы потока, является использование свойства:

property ReturnValue: Integer;

Это служебная переменная, в которую можно записывать данные, например о количестве запусков потока. Свойство ReturnValue часто применяется совместно с методом:

function WaitFor: LongWord;

Функция предназначена для организации ожидания, пока какой-нибудь другой поток не выполнит поставленную перед ним задачу. Функция возвращает значение, содержащееся в свойстве ReturnValue ожидаемого потока.

Приоритет потока

Приоритет потока формируется двумя составляющими: приоритетом процесса, которому принадлежит поток, и непосредственно приоритетом этого потока. Приоритет потока определяется свойством:

property Priority: TThreadPriority;

Возможные значения, которые может принимать приоритет, приведены в таблице 21.2.

Таблица. Описание приоритета потока, класс TThreadPriority

Значение

Описание

tpIdle

Фоновый приоритет. Поток с таким приоритетом выполняется только при отсутствии хоть ка-

tpLowest

кой либо загрузки процессора.

Низкий приоритет.

tpLower

Пониженный приоритет.

tpNormal

Нормальный приоритет, соответствующий поведению обычной программы.

tpHigher

Повышенный приоритет. Использование потоков с таким или более высоким приоритетом

 

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

 

 

 

94

tpHighest

Высочайший приоритет. Применяется только для потоков, которые выполняются в кратчайшие

tpTimeCritical

промежутки времени.

Приоритет реального времени, будет обладать приоритетом большим, чем у многих процессов

 

операционной системы.

procedure TfrmMain.FormCreate(Sender: TObject); begin

Thread1:=TMyThread.Create(TRUE);

Thread1.Priority:=tpIdle;

Thread1.Resume; end;

Без особой на то необходимости старайтесь не создавать потоков, с приоритетом выше нормального. Такой поступок может привести к тому, что системные ресурсы будут перенацелены на обслуживание этой задачи, а это в свою очередь снизит общую производительность системы.

Время выполнения потока

Операционная система позволяет оценить время процессора затраченное на любой из потоков процесса. Для этого предназначен метод:

Function GetThreadTimes(hThread : THandle; var CreationTime, ExitTime, KernelTime, UserTime : TFileTime) : boolean;

Параметры и порядок применения метода аналогичен изученной в начале главы функции GetProcessTimes() за исключением того, что здесь первый параметр – дескриптор интересующего нас потока, а не процесса.

Синхронизация потока с методами VCL

Если из тела процедуры Execute() Вам необходимо осуществить вызов любого метода или обратиться к свойству компонента библиотеки VCL, то обязательно используйте синхронизирующий метод:

procedure Synchronize(Method: TThreadMethod);

Задача метода – защита от гонок потоков, он гарантирует, что к каждому объекту VCL одновременно получит доступ только один поток.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]