|
Usuários |
|
100 Usuários Online
|
|
[Artigos]
Threads no Delphi, por onde começar ? – Parte III |
Publicado por rboaro : Quinta, Fevereiro 21, 2013 - 09:06 GMT-3 (586 leituras)
comentar Enviar para um amigo Versão para impressão
|
Então vamos lá, ver um pouco mais sobre as Threads no Delphi. Nessa terceira parte, veremos como criar blocos protegidos em uma thread para, entre outras coisas, interagir com a GUI (Graphical User Interface) com uma maior segurança. Com o intuito de simplificar o estudo, faremos o já clássico exemplo das barras de progressão sendo manipuladas paralelamente.
Basicamente nosso exemplo será uma thread que receberá o ponteiro de um TProgressBar. Essa thread irá setar a posição do TProgressBar em zero e depois fará um laço onde o TProgressBar será preenchido até a sua posição final (propriedade max). Seguindo a linha de raciocínio da primeira parte do estudo, vamos criar uma nova thread com a seguinte estrutura: type
TProcInThread = class(TThread)
private
FPgbProgresso : TProgressBar;
procedure atualizarProgressBar;
procedure avancarProgressBar;
public
constructor Create(CreateSuspended : boolean; pgbProgresso : TProgressBar);
protected
procedure Execute; override;
end;
Seguindo a mesma lógica dos posts anteriores, nosso construtor não apresenta nenhuma novidade:
constructor TProcInThread.Create(CreateSuspended : boolean;pgbProgresso: TProgressBar);
begin
Self.FPgbProgresso := pgbProgresso;
inherited Create(CreateSuspended);
end;
O método avançarProgressBar() foi feito mais por uma questão de conveniência, porém, entrarei em maiores detalhes em um próximo post:
procedure TProcInThread.avancarProgressBar;
begin
Self.FPgbProgresso.StepBy(1);
end;
O método Execute() também não possui nada demais, somente a chamada para o método atualizarProgressBar(), onde toda nossa lógica vai estar:
procedure TProcInThread.Execute;
begin
atualizarProgressBar();
end;
Agora sim, vamos ver o método mais importante da nossa thread, o atualizarProgressBar():
procedure TProcInThread.atualizarProgressBar;
var
I: Integer;
begin
Self.FPgbProgresso.Position := 0;
for I := 0 to Self.FPgbProgresso.Max - 1 do
begin
if Self.Terminated then
abort();
Synchronize(Self.avancarProgressBar);
Sleep(100);
end;
end;
Coloquei o Sleep(100) para que fosse possível visualizar melhor a execução, fora isso, chegamos finalmente no X da questão, o método Synchronize(). Este método executa a chamada de um determinado método (passado como parâmetro) dentro da thread principal da aplicação (main thread), ou seja, seguindo o nosso exemplo, o comando :
Synchronize(Self.avancarProgressBar);
Faz com que o método avancarProgressBar() seja executado dentro da thread principal da aplicação, fazendo com que o procedimento do método seja protegido e não entre em conflito com outras threads que manipulem os mesmos dados que a thread atual.
Com isso, nossa thread já está pronta. Para vê-la em execução, vamos criar uma formulário com 3 TProgressBar e 2 TButtons. No private do formulário, crie 3 objetos do tipoTProcInThread que é a classe da nossa thread:
type
TForm1 = class(TForm)
ProgressBar1: TProgressBar;
ProgressBar2: TProgressBar;
ProgressBar3: TProgressBar;
Button1: TButton;
Button2: TButton;
private
oProcInThread1 : TProcInThread;
oProcInThread2 : TProcInThread;
oProcInThread3 : TProcInThread;
public
end;
Sete a propriedade Max dos TProgressBar em 50 e no Button1 faremos a iniciação das threads:
procedure TForm1.Button1Click(Sender: TObject);
begin
oProcInThread1 := TProcInThread.Create(true,ProgressBar1);
oProcInThread2 := TProcInThread.Create(true,ProgressBar2);
oProcInThread3 := TProcInThread.Create(false,ProgressBar3);
oProcInThread1.Start;
oProcInThread2.FreeOnTerminate := true;
oProcInThread2.Start;
end;
Note que a terceira thread (oProcInThread3) não foi criada como Suspended, sendo assim não é necessário chamar o método Start() para iniciar a execução da thread e a segunda thread (oProcInTread2) foi configurada para ser liberada da memória assim que termine sua execução.
Agora em nosso Button2, liberaremos as threads da memória, mas somente as que não são liberadas automaticamente:
if not oProcInThreadBarra2.Finished then
oProcInThreadBarra2.WaitFor();
FreeAndNil(oProcInThreadBarra2);
if not oProcInThreadBarra3.Finished then
oProcInThreadBarra3.WaitFor();
FreeAndNil(oProcInThreadBarra3);
Finalmente, basta executar o aplicativo e ver o resultado:

O uso de blocos protegidos em um ambiente multi-thread é uma questão muito delicada, não proteger seus blocos que possuam acesso a dados compartilhados (como por exemplo um objeto, uma tabela ou um componente visual) abre margem para a ocorrência de conflitos, enquanto que o ato de proteger muitos blocos (utilizando o synchronize() por exemplo) pode comprometer o desempenho da sua aplicação. As boas práticas recomendam que evitem ao máximo compartilhar recursos entre as threads e nos casos que não for possível, existem algumas técnicas para se fazer.
Eu particularmente não recomendo o uso do synchronize() se a intenção não seja interagir com componentes visuais, caso seja necessário proteger um bloco crítico existem outras opções como por exemplo o TCriticalSection ou o TMutex que serão temas para posts futuros.
Na próxima etapa do estudo, veremos um pouco sobre AnonymousThread e métodos anônimos.
|
|
Comentários | |
| | Comentários pertencem aos seus respectivos autores. Não somos responsáveis pelo seus conteúdos. |
|
|
Edição 112 |
|
|
50 Programas Fontes |
|
|
Produtos |
|
|