Lazarus free pascal - Thread


Önceki yazıyı okuyup, Lazarus'u kurdun ve Pascal (Delphi) ile ilgili biraz tecrübe kazandın diye düşünüyorum. Resimdeki formu oluşturmak için Project > New Project ... > Application menüsünü seçerek boş bir sayfa açıyoruz. Button1 ve Button2'yi sayfaya ekliyoruz. Buton'a çift tıklayarak aşağıdaki kodları buton tıklama olaylarına yazalım.


procedure TForm1.Button1Click(Sender: TObject);
var
  i:integer;
begin
  for i:= 1 to 1000000 do
  begin
    button1.caption:= inttostr(i); //integer'ı string'e çevirir  
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  showmessage('Buton2''den merhaba!');
end;      

Programı çalıştırınca, önce buton2'ye tıklayın. Mesaj penceresi ekrana gelecektir. Buton1'e tıklarsan, butonun ismine yazan sayaç çalışmaya başlayacaktır. Bu sırada program penceresi sürüklenemez. Buton2'ye tıklayınca, mesaj penceresinin gelmediği veya buton2'nin çalışmadığı görülecektir. Ana thread, sayaçla meşgül olduğundan kullanıcıdan gelen isteklere cevap vermemektedir. Bu sorunu gidermek için koda iki satır ekleyelim.


procedure TForm1.Button1Click(Sender: TObject);
var
  i:integer;
begin
  for i:= 1 to 1000000 do
  begin
    button1.caption:= inttostr(i);
    Application.ProcessMessages; //kullanıcı isteklerini kontrol eder
    if Application.Terminated then exit; //pencerenin kapatılabilmesi için
  end;
end;     

Programı çalıştırınca, artık bir sorun kalmadı diye düşünebilirsin. Ama bir sorun var.. Sayaç çalışıyorken, buton2, cevap verecektir amma ve lakin mesaj penceresi geldiğinde, sayaç değerinin durduğunu göreceksin. Yine sayaç çalışırken, pencere başlığına tıklayarak pencereyi sürüklemeyi denediğinde, aynı sorun oluşacaktır. Bu noktada, sayacı ayrı bir thread olarak çalıştırmak daha doğru olacaktır. (Burada sayaç, uzun sürecek bir işlemi ifade etmek için kullanıldı, yoksa pratikte bu program kimsenin işine yaramaz)

Thread

Yukarıda yazılan program, tek thread'ten oluşmaktadır. Buna ana thread deniyor. Sayacı ayrı bir thread olarak çalıştırırsak, aynı programda iki thread çalışacaktır. Thread'lere, birbirinden bağımsız olarak, aynı program içinde çalışan, programcıklar diyebiliriz. İşletim sistemi, biraz onu biraz bunu çalıştırarak, bize aynı anda çalışıyorlar izlenimini vermektedir.


Soldaki ana thread, sağdaki sayaç thread'i gibi düşünebilirsin. Butonlar, ana thread'e aittir. Sayaç thread'i, buton1 başlığını direk olarak değiştiremez. Yani bunlar direk haberleşemezler. Haberleşme için, sayaç thread'i içinde Synchronize komutu kullanılır. Bu komut, senkronize etme işini veya "ana thread'e selamımı söyle, buton1'in başlığını değiştirsin" anlamında bir işlem yapar.


unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);

  private
    procedure OnThreadDone(Sender : TObject);
  public

  end;

  { Th }

  Th = Class(TThread)
  protected
    procedure execute; override;
  public
    constructor create(createSuspended : boolean);
  private
    sayac : integer;
    procedure UpdateForm;
  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ Th }

procedure Th.execute;
var
  i : integer;
begin
 for i:= 1 to 25000 do
  begin
    sayac:= i;
    Synchronize(@UpdateForm); // @ prosedür adresini belirtir
  end;
end;

constructor Th.create(createSuspended : boolean);
begin
  FreeOnTerminate:= true; // thread bitince kullanılan hafıza temizlensin
  inherited create(createSuspended);
end;

procedure Th.UpdateForm;
begin
  form1.button1.caption:= inttostr(sayac);
end;

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var
  T : Th;
begin
  button1.enabled := false; // buton1 tekrar tıklanamasın
  T:= Th.create(true);
  T.OnTerminate:= @OnThreadDone;
  T.Start; // Th.create(false) yazılırsa bu satıra gerek kalmaz
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  showmessage('Buton2''den merhaba!');
end;

procedure TForm1.OnThreadDone(Sender : TObject);
begin
  button1.enabled := true;
  showmessage('Sayaç thread''i sonlandı...')
end;

end.

Programı çalıştırınca, sayaçta asla bir donma olmayacaktır. Böylece form ve sayaç ayrı thread'ler olarak mutlu mesut çalışırlar.

Not: Type altında thread class'ın tanımlandığı yerde UpdateForm prosedürü veya constructor'ı tıkladıktan sonra Ctrl+Shift+C tuşlarına basılırsa implementation altında otomatik olarak boş prosedür kodları oluşur.

Yorumlar