Есть такой метод в одном стареньком компоненте:
procedure TTangentThread.SetActive(Value: Boolean);
begin
if Value <> FActive then
begin
FActive := Value;
case FActive of
False:
begin
FThread.Suspend;
if (IsRuntime) and (Assigned(FOnSuspend)) then FOnSuspend(self);
end;
True:
begin
FThread.Resume;
if (IsRuntime) and (Assigned(FOnResume)) then FOnResume(self);
end;
end;
end;
end;
И он, в общем то, работает, но компилятор говорит, что Suspend и Resume устарели и их лучше не использовать. Как это нынче правильно переписать, не используя "устаревших" методов?
Ответ
Suspend и Resume на самом деле выполняются операционной системой. Для Windows внутри этих методов вызываются SuspendThread и ResumeThread
Функции "живее всех живых", но... использовать их действительно не стоит. Проблема в том, что они работают с потоком "здесь и сейчас". То есть - если команда Suspend застала поток посередине выполнения оператора (например - присвоения строки) то именно в этой точке поток заснет.
Естественно, такая "произвольность" не способствует качественному выполнению приложения, особенно - если дополнительный и основной поток работают над разделяемыми (совместными) данными. Например, дополнительный поток начал изменять массив (строку, список и т.п.), в это время его "заснули", основной поток обнулил массив и разбудил дополнительный. В дополнительном потоке штатная проверка размерности уже пройдена, поэтому он не узнает о том, что того элемента, над которым поток пытается работать, уже нет. В результате - гарантированная порча памяти со всеми вытекающими последствиями.
Именно поэтому нужно использовать объекты синхронизации: TCriticalSection, TMutex, TEvent, TSemaphore, TMultiReadExclusiveWriteSynchronizer, которыми ограждается доступ к разделяемым ресурсам и / или посылаются "правильные", в "нужные моменты времени" управляющие сигналы. Альтернатива (не кросс-платоформенная) - использовать механизм сообщений
В качестве примера: дополнительный поток по команде должен выполнить какую-то работу и ожидать новую команду:
uses
System.SyncObjs;
type
TmyThread = class(TThread)
private
FEvent: TEvent; // "синхронизатор"
procedure DoWork; // в этом методе будет выполняться полезная работа
protected
procedure Execute; override;
public
constructor Create;
destructor Destroy; override;
procedure StartSingleWork; // этот метод вызывается "извне"
end;
{ TmyThread }
constructor TmyThread.Create;
begin
inherited Create(False);
FEvent := TEvent.Create; // создаем сигнальное событие
end;
procedure TmyThread.Execute;
begin
while not Terminated do
begin
FEvent.WaitFor; // ожидаем, пока взведут событие
FEvent.ResetEvent; // сбрасываем событие,чтобы
// опять войти в ожидание на WaitFor
if not Terminated then // и если поток не уничтожают
DoWork; // делаем свое дело.
end;
end;
procedure TmyThread.StartSingleWork;
begin
// кто-то извне хочет, чтобы поток выполнил свою работу
FEvent.SetEvent; // выводим поток из спячки.
// поток будет выведен из ожидания в WaitFor,
// выполнит полезную работу и опять заснет.
end;
procedure TmyThread.DoWork;
begin
// здесь выполняется какая-то полезная работа
// в контексте нашего дополнительного потока.
end;
destructor TmyThread.Destroy;
begin
Terminate; // начинаем уничтожение
FEvent.SetEvent; // выводим поток из спячки
while not Finished do // ждем, пока он завершится
Sleep(0);
FreeAndNil(FEvent); // и уничтожаем содержимое потока.
inherited;
end;
В дополнение: на мой взгляд, лучшая из статей про многопоточность в Delphi (и не только). К сожалению - только в web.archive...
Upd. Старый форум Винграда ожил, прямая ссылка на статью
Комментариев нет:
Отправить комментарий