Difference between revisions of "Multithreaded Application Tutorial/pt"

From Lazarus wiki
Jump to navigationJump to search
Line 1: Line 1:
 
{{Multithreaded Application Tutorial}}
 
{{Multithreaded Application Tutorial}}
  
= Introdução =
+
== Introdução ==
 
Esta página irá tentar explicar com escrever e debugar uma aplicação multi-tarefa(multithread) com Free Pascal e Lazarus.
 
Esta página irá tentar explicar com escrever e debugar uma aplicação multi-tarefa(multithread) com Free Pascal e Lazarus.
  
Line 18: Line 18:
 
Outro uso, é para criar uma aplicação servidora(server application) que é abilitada para responder a vários clientes ao mesmo tempo.
 
Outro uso, é para criar uma aplicação servidora(server application) que é abilitada para responder a vários clientes ao mesmo tempo.
  
= Você necessita de multi-tarefa ? =
+
== Você necessita de multi-tarefa? ==
  
 
Se você é um novato em multi-tarefa e somente você quer fazer sua aplicação com melhor tempo de resposta enquanto sua aplicação processa grandes trabalhos, então multi-tarefa pode não ser, o eu você está procurando.
 
Se você é um novato em multi-tarefa e somente você quer fazer sua aplicação com melhor tempo de resposta enquanto sua aplicação processa grandes trabalhos, então multi-tarefa pode não ser, o eu você está procurando.
Line 29: Line 29:
 
* algoritmos e chamadas de bibliotecas, que não podem ser separadas em pequenas partes.
 
* algoritmos e chamadas de bibliotecas, que não podem ser separadas em pequenas partes.
  
=A classe TThread=
+
== A classe TThread ==
  
 
Os exemplos a seguir podem ser encontrados no diretório examples/multithreading/.
 
Os exemplos a seguir podem ser encontrados no diretório examples/multithreading/.

Revision as of 21:35, 25 September 2009

Deutsch (de) English (en) español (es) français (fr) 日本語 (ja) polski (pl) português (pt) русский (ru) slovenčina (sk) 中文(中国大陆)‎ (zh_CN)

Introdução

Esta página irá tentar explicar com escrever e debugar uma aplicação multi-tarefa(multithread) com Free Pascal e Lazarus.

Uma aplicação multi-tarefa(multithread) é uma que cria duas ou mais tarefas em execução que trabalham ao mesmo tempo.

Se você é novo em multi-tarefa, por favor leia o parágrafo "O que você necessita para multi-tarefa ?" para descobrir, se você realmente necessita disto. Você pode se salvar de um monte de dores de cabeça.


Uma das tarefas é chamada de tarefa princípal. A Tarefa Princípal é criada pelo sistema operacional, cada vez que nossa aplicação inicia. A Tarefa Principal deve ter somente tarefa que atualiza os componentes que faz interfaces com o usuário(senão, a aplicação pode cair).

A principal idéia é que a aplicação pode fazer algum processamento em plano de fundo(background - numa segunda tarefa) enquando o usuário pode continuar seu trabalho (usando a tarefa principal).

Outro uso das tarefas é somente para ter uma melhor resposta da aplicação. Se você cria uma aplicação, e quando o usuário pressiona um botão, a aplicação inicia o processamento (um grande trabalho) ... e enquanto processando, a tela para de responder, e dá ao usuário a impressão de que a aplicação está morta, que não é legal. Se o grande trabalho é executado numa segunda tarefa, a aplicação mantém-se respondendo(sempre) como se estisse inativa. Neste caso é uma boa idéia, antes iniciando a tarefa, para disabilitar os botões da janela para evitar o usuário iniciar mais que uma tarefa por trabalho.

Outro uso, é para criar uma aplicação servidora(server application) que é abilitada para responder a vários clientes ao mesmo tempo.

Você necessita de multi-tarefa?

Se você é um novato em multi-tarefa e somente você quer fazer sua aplicação com melhor tempo de resposta enquanto sua aplicação processa grandes trabalhos, então multi-tarefa pode não ser, o eu você está procurando.

Aplicações multi-tarefa sempre tem pesados debugs e eles são cada vez mais complexos. E em muitos casos você não precisa de multi-tarefa. Uma simples tarefa é o suficiente.

Multi-tarefa somente é necessário para:

  • bloquear handles, como comunicação na rede
  • usando múltiplos processadores de uma vez
  • algoritmos e chamadas de bibliotecas, que não podem ser separadas em pequenas partes.

A classe TThread

Os exemplos a seguir podem ser encontrados no diretório examples/multithreading/.

Para criar um aplicação multi-tarefa, a maneira mais fácil e usando a classe TThread. Esta classe permite a criação de uma thread adicional (ao lado da thread principal) de uma maneira simples. Normalmente você é obrigado a substituir apenas dois métodos: o construtor Create, e o método Execute. No construtor, você vai prepara a thread para funcionar. Você vai definir os valores iniciais das variáveis ou propriedades que você precisa. O construtor original da TThread requer um parâmetro chamado Suspended. Como você pode esperar, o ajuste Suspended = True impedirá que a thread execute automaticamente após a criação. Se Suspended = False, a thread irá executar logo após a criação. Se a thread é criada com Suspended = True, então ele será executado apenas depois que o método Resume for chamado.

A partir da versão 2.0.1 do FPC, TThread.Create tem um parâmetro implícito para o tamanho da pilha. Agora você pode alterar o tamanho predefinido de cada pilha, para cada thread que você criar, se você precisar. Procedimentos de chamadas recursivas é um bom exemplo. Se você não especificar o parâmetro de tamanho da pilha, o tamanho padrão da pilha do OS será usado.

Na substituição do método Execute, você escreverá o código que irá funcionar na thread.

A classe TThread tem uma importante propriedade: Terminated : boolean; Se a thread tem um loop ( isto é típico), o loop deve ser terminado quando Terminated for True (este tem valor False porpadrão). Por cada passagem, o valor do Terminated deve ser verificado e, se for True, então o loop deve ser encerrado tão rapidamente como é apropriado, após alguma limpeza necessária. Tenha em mente que o método Terminate não faz nada por padrão: o método Execute deve explicitamente implementar o suporte para ele finalizar o seu trabalho.

Como já explicamos anteriormente, a thread não deve interagir com componentes visuais. Atualizações para componentes visíveis deve ser feita dentro do contexto da thread principal. Para fazer isto, existe um método em TThread chamado Synchronize. Synchronize requer um método (este não leva nenhum parâmetro) como um argumento. Quando você chamar esse método através Synchronize(@MyMethod) a execução do thread será pausada, o código de MyMethod funcionará na thread principal, e a execução da thread será recomeçada então. O funcionamento exato de Synchronize depende da plataforma, mas basicamente é isto: afixa uma mensagem na fila de mensagem principal e vai dormir. Eventualmente a thread principal processa a mensagem e chama MyMethod. Desta forma MyMethod é chamado sem contexto, que não significa que seja durante um evento do mouse ou durante o evento Paint, mas depois. Após a thread principal ter executado MyMethod, desperta e processa a próxima mensagem. A thread então continua.

Há uma outra propriedade importante de TThread: FreeOnTerminate. Se esta propriedade é True, o objeto da thread é automaticamente liberado quando a execução da thread (método Execute) terminar. Se não a aplicação precisará ser liberada manualmente.

Exemplo:

<delphi>  

 Type
   TMyThread = class(TThread)
   private
     fStatusText : string;
     procedure ShowStatus;
   protected
     procedure Execute; override;
   public
     Constructor Create(CreateSuspended : boolean);
   end;
 constructor TMyThread.Create(CreateSuspended : boolean);
 begin
   FreeOnTerminate := True;
   inherited Create(CreateSuspended);
 end;
 procedure TMyThread.ShowStatus;
 // this method is executed by the mainthread and can therefore access all GUI elements.
 begin
   Form1.Caption := fStatusText;
 end;
 procedure TMyThread.Execute;
 var
   newStatus : string;
 begin
   fStatusText := 'TMyThread Starting...';
   Synchronize(@Showstatus);
   fStatusText := 'TMyThread Running...';
   while (not Terminated) and ([any condition required]) do
     begin
       ...
       [here goes the code of the main thread loop]
       ...
       if NewStatus <> fStatusText then
         begin
           fStatusText := newStatus;
           Synchronize(@Showstatus);
         end;
     end;
 end;

</delphi>

Na aplicação:

<delphi>

 var
   MyThread : TMyThread;
 begin
   MyThread := TMyThread.Create(True); // This way it doesn't start automatically
   ...
   [Here the code initialises anything required before the threads starts executing]
   ...
   MyThread.Resume;
 end;

</delphi>

Se você quer fazer sua aplicação mais flexível, você pode criar um evento para o thread; Desta forma o seu método sincronizado não será fortemente acoplado com um formulário ou classe específico: você pode anexar os ouvintes de eventos da thread. Aqui está um exemplo:

<delphi>  

 Type
   TShowStatusEvent = procedure(Status: String) of Object;

 

   TMyThread = class(TThread)
   private
     fStatusText : string;
     FOnShowStatus: TShowStatusEvent;
     procedure ShowStatus;
   protected
     procedure Execute; override;
   public
     Constructor Create(CreateSuspended : boolean);
     property OnShowStatus: TShowStatusEvent read FOnShowStatus write FOnShowStatus;
   end;

 

 constructor TMyThread.Create(CreateSuspended : boolean);
 begin
   FreeOnTerminate := True;
   inherited Create(CreateSuspended);
 end;

 

 procedure TMyThread.ShowStatus;
 // this method is executed by the mainthread and can therefore access all GUI elements.
 begin
   if Assigned(FOnShowStatus) then
   begin
     FOnShowStatus(fStatusText);
   end;
 end;

 

 procedure TMyThread.Execute;
 var
   newStatus : string;
 begin
   fStatusText := 'TMyThread Starting...';
   Synchronize(@Showstatus);
   fStatusText := 'TMyThread Running...';
   while (not Terminated) and ([any condition required]) do
     begin
       ...
       [here goes the code of the main thread loop]
       ...
       if NewStatus <> fStatusText then
         begin
           fStatusText := newStatus;
           Synchronize(@Showstatus);
         end;
     end;
 end;

</delphi>

Na Aplicação,

<delphi>

 Type
   TForm1 = class(TForm)
     Button1: TButton;
     Label1: TLabel;
     procedure FormCreate(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
   private
     { private declarations }
     MyThread: TMyThread; 
     procedure ShowStatus(Status: string);
   public
     { public declarations }
   end;

 

 procedure TForm1.FormCreate(Sender: TObject);
 begin
   inherited;
   MyThread := TMyThread.Create(true);
   MyThread.OnShowStatus := @ShowStatus;
 end;

 

 procedure TForm1.FormDestroy(Sender: TObject);
 begin
   MyThread.Terminate;
   MyThread.Free;
   inherited;
 end;

 

 procedure TForm1.Button1Click(Sender: TObject);
 begin
  MyThread.Resume;
 end;

 

 procedure TForm1.ShowStatus(Status: string);
 begin
   Label1.Caption := Status;
 end;

</delphi>