|
Usuários |
|
54 Usuários Online
|
|
[Artigos]
Threads no Delphi, por onde começar? – Parte I |
Publicado por rboaro : Segunda, Fevereiro 18, 2013 - 08:06 GMT-3 (957 leituras)
6 Comentários Enviar para um amigo Versão para impressão
|
Thread é um termo conhecido por qualquer “informata” que se preze, mas para quem não se recorda, qualquer aplicação utiliza no minimo uma thread (a thread principal ou main thread) onde o fluxo do processamento é executado, nessas aplicações os comandos são executados um por vez de forma sequencial. Até ai nada demais, mas a coisa passa a ficar interessante quando usamos mais de uma thread em uma aplicação, fazendo com que seja possível executar processamentos de forma paralela. No dia a dia usamos as threads para gerenciar tarefas que precisam ser executadas paralelamente, melhorar desempenho de processamentos, etc. Mas não se iluda, usar threads é simples, mas requer alguns cuidados.
Deixando a teoria e o blablabla de lado, vamos ver um pouco sobre como funcionam as threads no Delphi.
Começaremos criando um exemplo bem básico, faremos um método que escreve de 1 a N linhas em um arquivo de texto tendo o intervalo de 1 milissegundo a cada linha escrita, o nome deste arquivo sempre será hora, minuto, segundo e milésimo de criação e o numero de linhas escritas, ex: 14-30-01-200_total_linhas_1500.txt. Inicialmente criaremos esse método da forma comum e depois passaremos para uma thread, assim conseguiremos realizar algumas comparações no resultado final. Para a execução comum, faremos dessa forma: procedure escreverArquivoDeTexto(const dirArquivo: string;
linhasAImprimir: integer);
var
linhasArquivoTexto : TStringList;
I: Integer;
begin
linhasArquivoTexto := TStringList.Create;
try
for I := 1 to linhasAImprimir do
begin
linhasArquivoTexto.Add(format('Linha numero %d',[i]));
Sleep(1);
end;
linhasArquivoTexto.SaveToFile(Format('%s\%s_total_linhas_%d.txt',[dirArquivo,FormatDateTime('hh-mm-ss-zzz',Now),linhasAImprimir]));
finally
linhasArquivoTexto.Free;
end;
end;
Para testar esse método, faremos com que ele seja executado 3 vezes e analisaremos a diferença de tempo de gravação entre um e outro, lembrando que, como ainda não estamos trabalhando com threads, será escrito um arquivo por vez.
escreverArquivoDeTexto(ExtractFileDir(ParamStr(0)),10500);
escreverArquivoDeTexto(ExtractFileDir(ParamStr(0)),10200);
escreverArquivoDeTexto(ExtractFileDir(ParamStr(0)),10400);
Usei um numero grande de linhas para que fosse possível medir o desempenho. Ao executar a sequencia acima, tive os seguintes arquivos criados no diretório do executável de teste:
14-47-22-711_total_linhas_10500.txt
14-47-33-078_total_linhas_10200.txt
14-47-43-661_total_linhas_10400.txt
Sendo assim, podemos concluir que levou cerca de 10 segundos de diferença para cada arquivo. Faremos agora novamente essa rotina, porém, faremos com que ela seja executada dentro de uma thread.
Para facilitar nossas vidas, o Delphi já possui o esqueleto padrão para a criação de uma thread, basta ir em FILE -> NEW -> OTHER -> THREAD OBJECT, após selecionar Thread Object será solicitado o nome da classe da sua Thread (para nosso exemplo utilizei TArquivoTextoInThread) e pronto, teremos o seguinte código já montado em uma nova unit:
type
TArquivoTextoInThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
end;
Antes de voltarmos a falar sobre conceitos, vamos primeiro preparar a nossa classe para que ela escreva em um arquivo de texto. Sendo rápido e prático, criaremos dois atributos privados na classe, um referente ao número de linhas a serem escritas e outro referente ao diretório onde o arquivo será criado. Feito isso, passaremos a nossa rotina de criar o arquivo de texto para dentro desta classe, ficando desta forma:
type
TArquivoTextoInThread = class(TThread)
private
FLinhasAImprimir : integer;
FDirArquivo : String;
procedure escreverArquivoDeTexto();
function gerarNomeArquivo():string;
protected
procedure Execute; override;
public
constructor Create (const CreateSuspended : boolean; const linhasAImprimir : integer; const dirArquivo : string);
end;
Certo, eu fiz algumas coisas a mais, mas vamos por partes, primeiro, vamos ver como ficou nosso método escreverArquivoDeTexto().
procedure TArquivoTextoInThread.escreverArquivoDeTexto;
var
linhasArquivoTexto : TStringList;
I: Integer;
begin
linhasArquivoTexto := TStringList.Create;
try
for I := 1 to Self.FLinhasAImprimir do
begin
linhasArquivoTexto.Add(format('Linha numero %d',[i]));
Sleep(1);
end;
linhasArquivoTexto.SaveToFile(Self.gerarNomeArquivo());
finally
linhasArquivoTexto.Free;
end;
end;
Basicamente nada mudou, só para melhorar a legibilidade do código foi criado um método para gerar o nome do arquivo de texto, porém ele tem a mesma lógica utilizada anteriormente:
function TArquivoTextoInThread.gerarNomeArquivo: string;
begin
result := Format('%s\%s_total_linhas_%d.txt',[Self.FDirArquivo,FormatDateTime('hh-mm-ss-zzz',Now),self.FLinhasAImprimir]);
end;
Sendo assim, até aqui nenhuma novidade, mas agora vamos para um pouco de conceitos analisando o construtor que fiz para a classe:
constructor TArquivoTextoInThread.Create(const CreateSuspended: boolean;
const linhasAImprimir: integer; const dirArquivo : string);
begin
Self.FLinhasAImprimir := linhasAImprimir;
Self.FDirArquivo := dirArquivo;
Self.FreeOnTerminate := true;
inherited Create(CreateSuspended);
end;
A classe TThread possui uma propriedade booleana chamada FreeOnTerminate que quando alterada para true a thread é liberada automaticamente da memória após a sua execução. Outra coisa que é importante de notar é o parâmetro CreateSuspended que o construtor da classe TThread recebe, ele basicamente define se a thread será criada suspensa ou não, ou seja, se a thread será executada automaticamente após a instância ser criada (CreateSuspended = false) ou se será necessário iniciar a execução da thread de forma manual (CreateSuspended = true) através do método Start. Para completar a implementação da nossa classe, vamos ver como ficou o método Execute() da thread:
procedure TArquivoTextoInThread.Execute;
begin
escreverArquivoDeTexto();
end;
Ok, imagino que você esperava mais do que simplesmente uma chamada para o método escreverArquivoDeTexto() porém, o segredo todo está ai, o método Execute() é exatamente o método que será executado assim que a thread for iniciada. Com isso, a nossa thread está pronta. Agora vamos fazer o mesmo teste que fizemos anteriormente mas dessa vez utilizando nossa thread:
var
oArquivoTextoInThread1 : TArquivoTextoInThread;
oArquivoTextoInThread2 : TArquivoTextoInThread;
oArquivoTextoInThread3 : TArquivoTextoInThread;
begin
oArquivoTextoInThread1 := TArquivoTextoInThread.Create(false,10500,ExtractFilePath(ParamStr(0)));
oArquivoTextoInThread2 := TArquivoTextoInThread.Create(false,10200,ExtractFilePath(ParamStr(0)));
oArquivoTextoInThread3 := TArquivoTextoInThread.Create(false,10400,ExtractFilePath(ParamStr(0)));
end;
Executando a rotina acima, como determinei que minha thread não irá iniciar suspensa, tive os seguintes arquivos criados no diretório do executável:
15-42-46-946_total_linhas_10200
15-42-47-167_total_linhas_10400
15-42-47-256_total_linhas_10500
Note que o último arquivo a ser criado foi o de 10500 linhas mesmo sendo o primeiro a ter sua rotina executada e outra coisa muito importante é a diferença de tempo na criação dos arquivos, se antes sem thread tivemos uma demora de 10 segundos para cada arquivo, aqui não passou de poucos milésimos, tivemos esse resultado pois cada arquivo demora cerca de 10 segundos para ser gerado, porém, como os 3 estavam sendo gerados ao mesmo tempo de forma paralela, não foi necessário utilizar uma fila de processamento.
Como o intuito deste post foi só demonstrar o uso de threads, não entrei em mais detalhes, porém, na próxima parte veremos um pouco mais sobre como controlar o ciclo de vida de uma thread.
|
|
Comentários | |
| | Comentários pertencem aos seus respectivos autores. Não somos responsáveis pelo seus conteúdos. |
por: marcoantoneo (marco@pgm.com.br)
: Fev 18, 2013 - 11:27 (Informações sobre o membro | Enviar uma mensagem)
|
Bom dia.
Francamente... Já tinha lido mta coisa sobre threads. Todos mto cansativos, usando logo de inicio mtas definições e expressões q para meu ver logo de inicio confundem mto o entendimento.
Com isso eu mesmo nunca utilizei tal artificio dentro da programação.
Acabei lendo o artigo por curiosidade e fiquei surpreso, pois, vi o quanto eh simples usar THREADS.
Parabens pela forma como o artigo foi desenvolvido e acho que eh por ae, iniciando com bons exemplos e ir desenvolvendo aos poucos o assunto. Estou ancioso aguardando por mais tópicos sobre o assunto.
|
por: drgarcia1986 (drgarcia1986@gmail.com) : Fev 18, 2013 - 02:01 (Informações sobre o membro | Enviar uma mensagem) http://drgarcia1986.wordpress.com | Olá Marco, como vai?
Fico realmente muito feliz que tenha gostado e fico também feliz por saber que consegui deixar o artigo de forma simples, exatamente para evitar essas coisas chatas que você falou.
Está série tem 5 partes, e provavelmente serão postadas aqui na active delphi durante essa semana.
Muito obrigado pelo feedback e espero que a partir de agora fique mais fácil entrar nesse mundo das threads =) | [ Comentários não permitidos para usuários anônimos. Por gentileza, registre-se ou conecte-se ao sistema
por: drgarcia1986 (drgarcia1986@gmail.com) : Fev 20, 2013 - 02:05 (Informações sobre o membro | Enviar uma mensagem) http://drgarcia1986.wordpress.com | Opa Ronaldo, tudo bom? muito obrigado pelo feedback. Estou verdadeiramente feliz pelo fato deste artigo estar tendo uma boa aceitação, a intensão foi realmente essa, tirar essa "capa misteriosa" sobre o assunto threads. Espero que gostem das outras partes também. =)
ps: Uma dica bacana para que se interessar sobre threads, meu "amigon" Mário Guedes do blog Eu Gosto do Delphi (http://eugostododelphi.blogspot.com.br/) está publicando um série de artigos sobre Threads na revista Active Delphi, vale a pena dar uma olhada | [ Comentários não permitidos para usuários anônimos. Por gentileza, registre-se ou conecte-se ao sistema
|
|
Edição 112 |
|
|
50 Programas Fontes |
|
|
Produtos |
|
|