Quando se programa uma thread existe uma questão importante a resolver que é como sinalizar a esta thread que há um processamento a ser feito.
Um bom exemplo disto é aquela thread que encapsula uma pilha de objetos que é alimentada por outra thread, logo ela só "precisa trabalhar" se houver objetos na pilha.
De cara, o código fica como no exemplo abaixo:
procedure TExemploEvent.Execute;
begin
inherited;
while not (Self.Terminated) do
begin
{$REGION 'Processamento da pilha'}
//Não é sempre que haverá elementos na pilha
if (Self.FQueue.Count > 0) then
begin
//Processamento da pilha
end;
{$ENDREGION}
end;
end;
O efeito colateral disto é um consumo intenso de CPU, o que obviamente é péssimo. A solução então é colocar um Sleep(10) - evite o Application.ProcessMessages dentro de uma thread. Com isso o código fica assim:
procedure TExemploEvent.Execute;
begin
inherited;
while not (Self.Terminated) do
begin
//Sleep para aliviar a thread
Sleep(10);
{$REGION 'Processamento da pilha'}
//Não é sempre que haverá elementos na pilha
if (Self.FQueue.Count > 0) then
begin
//Processamento da pilha
end;
{$ENDREGION}
end;
end;
Isso resolve o problema mas cria um desconforto: A thread fica em um loop insano e o Sleep(10) resolve parte do problema. Podemos nos contentar com isso ou fazer algo melhor.
Para isto podemos usar o TEvent, que será usado para sinalizar a Thread que há elementos na fila. Veja como fica o Execute neste novo contexto:
procedure TExemploEvent.Execute;
var
eEvent : TWaitResult;
begin
inherited;
while not (Self.Terminated) do
begin
//Esperando sinalização do evento
eEvent := Self.FEvent.WaitFor(INFINITE);
case eEvent of
wrSignaled:
begin
{$REGION 'Processamento da pilha'}
//Não é sempre que haverá elementos na pilha
if (Self.FQueue.Count > 0) then
begin
//Processamento da pilha
end;
{$ENDREGION}
end;
//...
end;
end;
Por fim, veja o exemplo completo.
unit Thread;
interface
uses
Classes, Contnrs, SyncObjs;
type
TExemploEvent = class(TThread)
private
FQueue: TObjectQueue;
FEvent: TEvent;
FCritical: TCriticalSection;
public
procedure AfterConstruction; override;
procedure BeforeDesttruction; override;
procedure Execute; override;
procedure AdicionarItem(poItem: TObject);
end;
implementation
uses
SysUtils;
{ TExemploEvent }
//É através deste método que outras threads colocarão objetos na pilha
procedure TExemploEvent.AdicionarItem(poItem: TObject);
begin
//Entra na seção crítica
Self.FCritical.Enter;
try
//Coloca o item na pilha
Self.FQueue.Push(poItem);
//Sinaliza o TEvent
Self.FEvent.SetEvent;
finally
//Sai da seção crítica
Self.FCritical.Release;
end;
end;
procedure TExemploEvent.AfterConstruction;
begin
inherited;
//Seção crítica para acessar a fila de objetos
Self.FCritical := TCriticalSection.Create;
//Fila de objetos que serão processados
Self.FQueue := TObjectQueue.Create;
//Sinalizador
Self.FEvent := TEvent.Create(nil,False,True,'_exemploevent');
// --- ----- ---- -------------
// | | | |
// | | | \............> Nome único para a instância, do contrário, será usado o já existente
// | | \......................> Podemos já criar SINALIZADO
// | \............................> Indica se será resetado MANUALMENTE ou AUTOMATICAMENTE
// \.................................> Atributos, nil basta na maioria das necessidades
end;
procedure TExemploEvent.BeforeDesttruction;
begin
inherited;
Self.FCritical.Free;
Self.FQueue.Free;
Self.FEvent.Free;
end;
procedure TExemploEvent.Execute;
var
eEvent : TWaitResult;
begin
inherited;
while not (Self.Terminated) do
begin
//Esperando sinalização do evento
eEvent := Self.FEvent.WaitFor(INFINITE);
case eEvent of
//Processa a pilha somente se o evento foi sinalizado
wrSignaled:
begin
{$REGION 'Processamento da pilha'}
//Não é sempre que haverá elementos na pilha
if (Self.FQueue.Count > 0) then
begin
//Entra na seção crítica
Self.FCritical.Enter;
try
while (Self.FQueue.Count > 0) do
begin
Sleep(10);
//Para efeitos de exemplificação, esta apenas livrando o objeto
Self.FQueue.Pop.Free;
end;
finally
//Sai da seção crítica
Self.FCritical.Release;
end;
end;
{$ENDREGION}
end;
wrTimeout:
begin
//Excedeu o tempo de espera
Continue;
end;
wrAbandoned:
begin
Abort;
end;
wrError:
begin
Abort;
end;
wrIOCompletion:
begin
Abort;
end;
end;
end;
end;
end.
E é isso ai.