Thread - Exemplos

DAVID CANDIDO DA SILVA

DAVID CANDIDO DA SILVA Publicado 14/11/2017 


Basta então criar uma Task para cada consulta e disparar todas elas?

Sim, mas há uma condição que exige a nossa atenção. Você deve ter notado que cada consulta tem uma duração diferente. Isso significa que, se executarmos todas elas em threads, é possível que as consultas mais demoradas não sejam finalizadas a tempo. Caso isso ocorra, os dados dessas seções não serão exibidos no relatório.
Para evitar esse comportamento, criaremos um array de ITask e, ao final, utilizaremos um método chamado WaitForAll para solicitar que o processamento principal só prossiga quando todas as threads forem concluídas. Confira a codificação:


procedure EmitirRelatorio;
var
  Tasks: array [0..2] of ITask;
    Inicio: TDateTime;
  Fim: TDateTime;
begin
  Inicio := Now;
    Tasks[0] := TTask.Create(ConsultarDadosCliente);
  Tasks[0].Start;
    Tasks[1] := TTask.Create(ConsultarDadosPedido);
  Tasks[1].Start;
    Tasks[2] := TTask.Create(ConsultarDadosItensPedido);
  Tasks[2].Start;      
  TTask.WaitForAll(Tasks);
    Fim := Now;
    ShowMessage(Format('Consultas realizadas em %s segundos.',
    [FormatDateTime('ss', Fim - Inicio)]));
end;


agora, ao executar o mesmo método de emissão, este é o resultado de tempo total:


////////////////////////////

Consultas realizadas em 04 segundos

///////////////////////////

outros exemplos?

Um fechamento de caixa é outro cenário bem comum no qual o Multithreading pode ser aplicado, já que consiste em uma série de operações, como conferência de valores, cálculo de entradas e saídas, geração de saldos e até a produção de gráficos. Mesmo assim, para dar continuidade sobre este recurso, vou apresentar mais um cenário. Imagine uma rotina de processamento de arquivos em lote através da seguinte codificação:


var
  ListaArquivos: TStringList;
  i: integer;
begin
  ListaArquivos := TStringList.Create;
    ListaArquivos.Text := CarregarArquivos;
    for i := 0 to Pred(ListaArquivos.Count) do
  begin
    ProcessarArquivo(ListaArquivos[i]);
  end;
   FreeAndnil(Free);
end;


Observamos, claro, que um arquivo será processado por vez, já que o loop é iterativo e sequencial. Esse mesmo procedimento também pode ser distribuído em threads paralelas, no entanto, para este caso, não utilizamos o TTask. Devemos utilizar um comando chamado TParallel.For, contida na mesma biblioteca. Além disso, como haverá um acesso concorrente ao objeto ListaArquivos, é necessário utilizar um mecanismo de semáforo, representado pelo método TThread.Queue no código abaixo:


var
  ListaArquivos: TStringList;
begin
  ListaArquivos := TStringList.Create;
    ListaArquivos.Text := CarregarArquivos;
    TParallel.For(0, Pred(ListaArquivos.Count),
            procedure (i: integer)
            begin
              TThread.Queue(TThread.CurrentThread,
                procedure
                begin
                  ProcessarArquivo(ListaArquivos[i]);
                end)
            end);
end;


Explicando: ao invés de usar um único fluxo de processamento, o TParallel.For distribuirá as iterações em threads, ou seja, o loop será “dividido” em fluxos paralelos, reduzindo (bastante!) o tempo.

 

Fico por aqui, pessoal! Espero que esse artigo incentive a utilização dessa poderosa biblioteca do Delphi.
Um grande abraço!

David Candido


Voltar ao topo