Escolar Documentos
Profissional Documentos
Cultura Documentos
processamento paralelo
17/04/2017 por Andr L. Celestino8 Comentrios
Fala, galera!
Acredito que muitos de vocs j tenham usado, estudado ou ao menos ouvido falar de
Multithreading do Delphi. As bibliotecas deste recurso esto presentes desde a verso XE7,
porm, por serem relativamente recentes, s vezes esquecemos de sua existncia. O objetivo
deste artigo apresentar um cenrio no qual o uso de Multithreading pode trazer uma grande
vantagem em relao ao tempo de resposta de uma aplicao.
Em maro deste ano, apliquei um treinamento sobre Delphi Seattle na DB1 Global Software,
empresa em que eu trabalho, para apresentar os recursos das verses mais recentes do Delphi,
entre eles, claro, a biblioteca de Multithreading. Com esse mecanismo, podemos distribuir
processamentos complexos em threads distintas, executando-as ao mesmo tempo. Por
estarem em fluxos paralelos, o tempo de trmino destes processamentos evidentemente
menor do que uma abordagem sequencial (ou linear).
Muitos me questionaram sobre a aplicao de um processamento paralelo em um cenrio
mais prximo da realidade, portanto, dediquei os ltimos dias para elaborar este artigo.
Considere a emisso de um relatrio de pedido que possui as seguintes sees:
Para que a emisso seja realizada, necessrio consultar os dados dessas trs sees de forma
separada. Como exemplo, considere que cada consulta demore aproximadamente os tempos
abaixo:
ConsultarDadosCliente;
ConsultarDadosPedido;
ConsultarDadosItensPedido;
Para simular o tempo dispendido com essa operao, programei um Sleep() dentro de cada
mtodo com a respectiva durao:
procedure ConsultarDadosCliente;
begin
Sleep(2000);
end;
procedure ConsultarDadosPedido;
begin
Sleep(3000);
end;
procedure ConsultarDadosItensPedido;
begin
Sleep(4000);
end;
Em seguida, adicionei tambm duas variveis para calcular o tempo gasto e uma mensagem
para exibi-lo ao trmino das consultas:
procedure EmitirRelatorio;
var
Inicio: TDateTime;
Fim: TDateTime;
begin
Inicio := Now;
ConsultarDadosCliente;
ConsultarDadosItensPedido;
ConsultarDadosPedido;
Fim := Now;
end;
O propsito deste artigo mostrar que podemos melhorar isso. Com o recurso de
processamento paralelo, podemos distribuir cada consulta em uma thread e execut-las
simultaneamente. Para isso, trabalharemos com a classe TTask, da
unit System.Threading, utilizando a seguinte sintaxe:
uses
System.Threading;
{ ... }
var
Task: ITask;
begin
Task := TTask.Create({mtodo});
Task.Start;
end;
Basta ento criar uma Task para cada consulta e disparar todas elas?
Sim, mas h uma condio que exige a nossa ateno. Voc deve ter notado que cada consulta
tem uma durao diferente. Isso significa que, se executarmos todas elas em threads,
possvel que as consultas mais demoradas no sejam finalizadas a tempo. Caso isso ocorra,
os dados dessas sees no sero exibidos no relatrio.
Para evitar esse comportamento, criaremos um array de ITask e, ao final, utilizaremos um
mtodo chamado WaitForAll para solicitar que o processamento principal s prossiga
quando todas as threads forem concludas. Confira a codificao:
procedure EmitirRelatorio;
var
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;
end;
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;
ListaArquivos.Free;
end;
Observamos, claro, que um arquivo ser processado por vez, j que o loop iterativo e
sequencial. Esse mesmo procedimento tambm pode ser distribudo em threads paralelas, no
entanto, para este caso, no utilizamos o TTask. Devemos utilizar um
comando chamado TParallel.For, contida na mesma biblioteca. Alm disso, como
haver um acesso concorrente ao objeto ListaArquivos, necessrio utilizar um
mecanismo de semforo, representado pelo mtodo TThread.Queue no cdigo 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;
Fico por aqui, pessoal! Espero que esse artigo incentive a utilizao dessa poderosa biblioteca
do Delphi.
Um grande abrao!