Este exemplo mostra como implementar um mecanismo de pausa e reinício, bem como uma forma de fazer uma thread esperar um tempo antes de continuar sua execução cíclica (timeout)
- program SuperThread;
- {$APPTYPE CONSOLE}
- {$R *.res}
- uses
- System.SysUtils, System.SyncObjs, System.Classes;
- type
- TMinhaThread = class (TThread)
- private
- FPauseRestart: TEvent;
- FTimeOut: TEvent;
- FTimeOutValue: Byte;
- protected
- procedure Execute; override;
- public
- constructor Create(ATimeOutValue: Byte); reintroduce;
- destructor Destroy; override;
- procedure Pause;
- procedure Restart;
- end;
- { TMinhaThread }
- constructor TMinhaThread.Create(ATimeOutValue: Byte);
- begin
- inherited Create(True);
- FPauseRestart := nil;
- FTimeOut := nil;
- FTimeOutValue := ATimeOutValue;
- end;
- destructor TMinhaThread.Destroy;
- begin
- // Configura a propriedade Terminated como True fazendo com que os pontos de
- // verificação normais do loop da thread a façam terminar "graciosamente"
- Terminate;
- // Caso a thread esteja parada no seu timeout, faz com que a função WaitFor
- // retorne, o que faz a thread continuar sua execução. Como o WaitFor de
- // timeout é o último comando do loop da thread, a próxima linha a ser
- // executada é a condição do loop da thread (not Terminated). Como já
- // configuramos Terminated como true, o loop termina e o método execute da
- // thread também. Adicionalmente, o bloco abaixo libera FTimeOut da memória
- if Assigned(FTimeOut) then
- begin
- FTimeOut.SetEvent;
- FTimeOut.Free;
- end;
- // Caso a thread esteja pausada, faz com que a função WaitFor retorne, o que
- // faz a thread continuar sua execução. Como o WaitFor de pausa é o primeiro
- // comando do loop da thread, a próxima linha a ser executada é a verificação
- // adicional (dentro do loop) para saber se a thread não foi terminada. Como
- // já configuramos Terminated como true, o bloco de instruções úteis da thread
- // não será executado e o fluxo de execução volta para a condição do loop (not
- // Terminated), que retorna False novamente e termina o loop e
- // consequentemente o método execute. Adicionalmente, o bloco abaixo libera
- // FPauseRestart da memória
- if Assigned(FPauseRestart) then
- begin
- FPauseRestart.SetEvent;
- FPauseRestart.Free;
- end;
- inherited;
- end;
- procedure TMinhaThread.Execute;
- var
- WR: TWaitResult;
- begin
- // Este TEvent serve apenas para pausar e reiniciar a thread. Enquanto a
- // thread está no estado de não pausada, o while mais adiante, fica rodando
- // indefinidamente enquanto a thread não estiver terminada. Quando a thread é
- // pausada, o fluxo fica parado na linha do WaitFor indefinidamente, sem
- // consumir recursos e a única forma de sair desse estado é despausando a
- // thread, que em termos práticos significa "assinalar" o evento, usando o
- // método SetEvent. Um evento "assinalado" é o mesmo que um evento que ocorre,
- // portanto, ao assinalar um evento, o método WaitFor é terminado e o retorno
- // é wrSignaled. Enquanto o evento não for novamente pausado (ResetEvent), ele
- // se mantém assinalado e por isso a linha do WaitFor retorna imediatamente
- // com um valor de retorno = wrSignaled. Este comportamento só é obtido porque
- // o segundo parâmetro do construtor do TEvent, indica que ele é ressetado
- // manualmente. Se este parâmetro indicasse que ele é ressetado
- // automaticamente, o evento seria "desassinalado" imediatamente após ele ter
- // sido "assinalado", isto é, chamadas subsequentes a WaitFor iriam ficar
- // "travadas" até que o evento fosse assinalado novamente
- FPauseRestart := TEvent.Create(nil,True,True,'Pause/Restart');
- // --- ---- ---- -----
- // | | | |
- // | | | \......> Nome único para a instância, do contrário, será usado o já existente
- // | | \............> Podemos já criar SINALIZADO (no caso isso significa que a thread não estará pausada inicialmente)
- // | \.................> Indica se será resetado MANUALMENTE ou AUTOMATICAMENTE
- // \......................> Atributos, nil basta na maioria das necessidades
- // Este TEvent serve para que a tread fique aguardando por um determinado
- // período de tempo antes de continuar o seu loop e ainda permite que a
- // qualquer momento esta "pausa temporizada" seja interrompida a fim de
- // permitir que a thread seja finalizada "graciosamente"
- FTimeOut := TEvent.Create(nil,False,False,'TimeOut');
- while not Terminated do
- begin
- WR := FPauseRestart.WaitFor(INFINITE);
- // Após sair da pausa, é necessário verificar se a thread ainda precisa ser
- // executada
- if not Terminated then
- case WR of
- // A única forma válida de saída do estado de pausa que permite que o
- // código da thread seja executado é se ela foi explicitamente
- // despausada, o que implica WR = wrSignaled. Em qualquer outro caso, o
- // código da thread não deve ser executado
- wrSignaled: begin
- { ================================================================== }
- { AQUI VAI O SEU CÓDIGO ÚTIL, ISTO É, AQUILO QUE DEVE SER EXECUTADO }
- { POR ESTA THREAD EM SEGUNDO PLANO }
- { ================================================================== }
- FTimeOut.WaitFor(FTimeOutValue * 1000);
- end;
- end;
- end;
- end;
- procedure TMinhaThread.Pause;
- begin
- if Started and Assigned(FPauseRestart) then
- FPauseRestart.ResetEvent;
- end;
- procedure TMinhaThread.Restart;
- begin
- if Started and Assigned(FPauseRestart) then
- FPauseRestart.SetEvent;
- end;
- var
- MinhaThread: TMinhaThread;
- begin
- try
- // Exte exemplo funciona corretamente com threads que não são
- // FreeOnTerminate. É necessário destruir a instância da thread, a qual vai
- // executar o seu destrutor interno e parar o funcionamento da mesma de
- // forma ordenada
- MinhaThread := TMinhaThread.Create(60); // 60 segundos
- MinhaThread.Start;
- Sleep(5000);
- MinhaThread.Pause;
- Sleep(5000);
- MinhaThread.Restart;
- Sleep(5000);
- MinhaThread.Free;
- except
- on E: Exception do
- Writeln(E.ClassName, ': ', E.Message);
- end;
- end.