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.