Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Martin Harvey - Threads.doc
Скачиваний:
7
Добавлен:
01.03.2025
Размер:
1.36 Mб
Скачать

Контролируемое завершение потока - Подход 2

В этом примере мы модифицируем код программы для простых чисел так, чтобы в каждый момент выполнялся только один поток, и программа перед выходом будет ждать завершения потока. Хотя в этой программе и не обязательно основному потоку ждать завершения других, но упражнение будет полезным и продемонстрирует несколько свойств WaitFor, которые не всегда желательны, а также проиллюстрирует пару довольно тонких моментов, которые могут быть упущены новичками в программировании потоков. Сначала код главной формы

unit PrimeForm;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

StdCtrls, PrimeThread;

Const

WM_THREAD_COMPLETE = WM_APP + 5437; { Just a magic number }

Type

TPrimeFrm = class(TForm)

NumEdit: TEdit;

SpawnButton: TButton;

ResultsMemo: TMemo;

procedure SpawnButtonClick(Sender: TObject);

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

private

FThread: TPrimeThrd;

procedure HandleThreadCompletion(var Message: TMessage);

message WM_THREAD_COMPLETE;

end;

var

PrimeFrm: TPrimeFrm;

Implementation

{$R *.DFM}

procedure TPrimeFrm.HandleThreadCompletion(var Message: TMessage);

begin

if Assigned(FThread) then

begin

FThread.WaitFor;

FThread.Free;

FThread := nil;

end;

end;

procedure TPrimeFrm.SpawnButtonClick(Sender: TObject);

begin

if not Assigned(FThread) then

begin

FThread := TPrimeThrd.Create(True);

FThread.FreeOnTerminate := false;

Try

with FThread do

begin

TestNumber := StrToInt(NumEdit.Text);

Resume;

end;

except on EConvertError do

begin

FThread.Free;

FThread := nil;

ShowMessage('That is not a valid number!');

end;

end;

end;

end;

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

begin

CanClose := true;

if Assigned(FThread) then

begin

if MessageDlg('Threads active. Do you still want to quit?',

mtWarning, [mbYes, mbNo], 0) = mrNo then

CanClose := false;

end;

{Sleep(50000);}{Line C}

if CanClose then

begin

if Assigned(FThread) then

begin

FThread.Terminate;

FThread.WaitFor;

FThread.Free;

FThread := nil;

end;

end;

end;

end.

Можно увидеть несколько отличий от предыдущего примера:

В начале модуля объявлено "магическое число" . Это относительный номер сообщения, а значение его не важно, главное, чтобы оно было уникальным.

Вместо счетчика потоков мы следим только за одним потоком, которому отвечает переменная FThread главной формы.

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

Код создания потока не устанавливает свойство FreeOnTerminate в True, вместо этого основной поток VCL будет освобождать рабочий поток позже.

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

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

Учтите эти пункты, а здесь код рабочего потока

unit PrimeThread;

interface

uses

Classes;

Type

TPrimeThrd = class(TThread)

Private

FTestNumber: integer;

FResultString: string;

Protected

function IsPrime: boolean;

procedure UpdateResults;

procedure Execute; override;

public

property TestNumber: integer write FTestNumber;

end;

implementation

uses SysUtils, Dialogs, PrimeForm, Windows;

procedure TPrimeThrd.UpdateResults;

begin

PrimeFrm.ResultsMemo.Lines.Add(FResultString);

end;

function TPrimeThrd.IsPrime: boolean;

var

iter: integer;

begin

result := true;

if FTestNumber < 0 then

begin

result := false;

exit;

end;

if FTestNumber <= 2 then

exit;

iter :=^2;

while^(iter^<^FTestNumber) and (not terminated) do {Line A}

begin

if (FTestNumber mod iter) = 0 then

begin

result := false;

{exit;}

end;

Inc(iter);

end;

end;

procedure TPrimeThrd.Execute;

begin

if IsPrime then

FResultString := IntToStr(FTestNumber) + ' is prime.'

Else

FResultString := IntToStr(FTestNumber) + ' is not prime.';

if not Terminated then {Line B}

begin

Synchronize(UpdateResults);

PostMessage(PrimeFrm.Handle, WM_THREAD_COMPLETE, 0, 0);

end;

end;

end.

В нем тоже есть небольшие отличия от того, что было в Главе 3:

  • Функция IsPrime теперь проверяет запросы на завершение потока, обеспечивая быстрый выход, если установлено свойство Terminated.

  • Процедура Execute делает проверку на нештатное завершение.

  • При нормальном завершении используется Synchronize для показа результатов и главной форме посылается сообщение - запрос на освобождение потока.

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