Derinlemesine Threading..(2)

// 20 Temmuz 2009 // Delphi, İşletim Sistemi, Programlama, Win32

Bir önceki makalemizde thread’ler konusuna giriş yapmış, işletim sisteminin thread’leri nasıl yönettiğini ve thread’lerin senkronizasyon mekanizmalarını anlatmaya çalışmıştık. Bu bağlamda Critical Section, Mutex, Semaphore senkronizasyon mekanizmalarını izah etmiştik. İlk makalemizde değinmediğimiz 2 adet senkronizasyon mekanizmasına da bu makalede temas etmeye çalışacağız. Event ve Waitable Timer adı verilen bu mekanizmalar da tıpkı diğer senkronizasyon mekanizmaları gibi çalışırlar. Bu iki sekonronizasyon mekanizması da WaitForSingleObject yada WaitForMultipleObjects vasıtası ile thread’lerimizin belirli kod bloklarında belirli bir şart sağlanana kadar beklemesi için vardırlar.

Hatırlayacağımız üzere, critical section’lar belirli bir kritik kod bloğuna aynı anda birden fazla thread’in girmesine müsaade etmiyordu. Mutex’ler ise critical section’lara son derece benzemelerine rağmen birden fazla uygulamanın(process) thread’lerinin de aynı kod bloklarına girişlerini senkronize ediyordu. Ardından temas ettiğimiz semaphore’ler ise biraz daha farklı bir yaklaşım ile kritik bir kod bloğuna bizim belirlediğimiz sayıda thread’in girmesini sağlıyordu.

Event mekanizmaları da yukarıda sayılan thread senkronizasyon mekanizmaları gibi çalışırlar. Ancak elbette kendine özgü tarafları da vardır. Programlarımızda hangi thread senkronizasyon mekanizmasını kullanacağımız tamamen ihtiyaçlarımız ile doğru orantılıdır. Birinin bir diğerine üstünlüğü gibi bir şey söz konusu değildir. Tüm bu bahsedilen mekanizmaların asıl amacı, işletim sistemindeki thread geçişlerinin 20 ms. olduğu bir ortamda veriye hatasız bir şekilde erişmek ve kullanabilmektir.

Event mekanizmaları, birden fazla thread’in ortaklaşa çalışması söz konusu olduğunda anlamlı olurlar. Bir thread’in bir diğer thread’i beklemesi gerektiğinde, kısaca bir ekip ruhunun gerektiği noktalarda bu mekanizma son derece kullanışlıdır. Eğer kodlarınızın bir ekip ruhu ile çalışması gerekiyor ise, aralarında bir imece söz konusu ise o halde bu mekanizmayı bilmeniz faydalı olacaktır.

Event mekanizmasını anlatabilmek adına, mübarek miraç kandili vesilesi ve ramazan ayının yaklaşıyor olması münasebeti ile Ramazan ayına has bir örnek tercih ettim. Bu örneğimiz; iftar saatine yakın bir zaman diliminde evimizde geçen olaylara ilişkin bir senaryoyu içeriyor.

Evde; anne, baba, çocuk ve bir misafirimiz var. Senaryomuz gereği; iftara yarım saat kala anne yemekleri ısıtmaya başlar. Yemeklerin ısınmasına başlanılması ile baba sofra kurma hazırlıklarına girişir. İftara 5 dk kala, evin küçük çocuğu pide almak üzere evden dışarı çıkar. Ezan’ın okunması ile tüm ev ahalisi iftarlarını açarlar.

Bu küçük ama ramazan ayında hemen hemen hepimizin yaşadığı senaryonun bilgisayara aktarılması sırasında thread’ler ve event senkronizasyon mekanizması kullanacağız. İftar saati gelene kadar tüm ev ahalisinin sohbet ettiğini yada televizyon seyrettiğini varsayıyoruz. İftar’a yarım saat kala anne thread bir event vasıtası ile tetikleniyor ve yemekleri ısıtmaya başlıyor. Ardından baba thread, anne thread tarafından sofrayı kurması için uyarılıyor. Anne ve baba thread’ler üstlerine düşen vazifeleri yaparlarken, diğer ev ahalisi hâla sohbet ve televizyon izleme ile iştigal ediyorlar.

İftara 5 dakika kala, çocuk thread’imiz evden pide almak üzere ayrılıyor. Misafirimiz bu arada televizyon izlemek ile meşgul. Çocuk pide almaktan gelince, baba sofrayı kurmuş, anne yemekleri ısıtmış durumda. Ve hep birlikte ezanın okunmasını bekliyorlar. Ezanın okunmasına müteakip; tüm ev ahalisi oruçlarını açıyorlar.

Senaryomuz hakkında özet bir bilgi verdikten sonra, event senkronizasyon mekanizmasında kullanılan metodların tanımlarına bir bakalım isterseniz;

HANDLE CreateEvent(
    LPSECURITY_ATTRIBUTES lpEventAttributes,	
    BOOL bManualReset,	
    BOOL bInitialState,	
    LPCTSTR lpName 	
   );

HANDLE OpenEvent(
    DWORD dwDesiredAccess,	
    BOOL bInheritHandle,	
    LPCTSTR lpName 	
   );

BOOL SetEvent(
    HANDLE hEvent
   );

BOOL ResetEvent(
    HANDLE hEvent
   );

ve Delphi tanımlarımız:

function CreateEvent(lpEventAttributes: PSecurityAttributes;
  bManualReset, bInitialState: BOOL; lpName: PChar): THandle; stdcall;

function OpenEvent(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PChar): THandle; stdcall;

function SetEvent(hEvent: THandle): BOOL; stdcall;

function ResetEvent(hEvent: THandle): BOOL; stdcall;

Tanımlarından gördüğünüz gibi daha önce anlattığımız senkronizasyon nesneleri ile bir farklılıkları görülmüyor. Hatırladığınız üzere senkronizasyon nesnelerini bir kapıya benzetmiştik. Kapının açık yada kapalı olması durumları gibi senkronizasyon nesneleri de açık yada kapalı durumlarına sahiptirler. CreateEvent API fonksiyonunun ikinci parametresi olan bManualReset kapının açılma kapanma işinin otomatik mi yoksa bize mi bırakılacağının kararını içeriyor. Bu parametreye true geçilmesi, SetEvent metodu ile açık duruma geçen kapının ResetEvent ile kapatılmasını bizim kontrol etmemiz gerektiğini ifade ediyor. Bu parametreye false geçilmesi durumunda, kapının açık olduğunu görüp ilgili kod bloğunu geçen thread kapıyı ardından otomatikman kapatacak ve başka thread’ler kapının önünde beklemeye devam edeceklerdir. Biz örneğimizde bu parametrenin her iki durumunuda kullanacağız.

Tanımları verdiğimize ve bu tanımların diğer senkronizasyon mekanizmalarındaki tanımlarla büyük benzerlikler içerdiğini söylediğimize göre, artık kod örneğimize geçebiliriz:

(*
Ramazan ayı senaryomuz
------------------------------------------------
Anne yarım saat kala yemekleri ısıtmaya başlasın
Baba yarım saat kala sofrayı kurmaya yardım etsin
Çocuk 5 dk kala pide almaya gitsin
Misafir yan gelip yatsın

Ezan okunsun
Herkes orucunu açsın
Anneye teşekkür edilsin(En önemli kısım bu, bunu yapmazsanız anne sizi Terminate eder.)
*)

  TEvAhalisiThread = class(TThread)
  public
    constructor Create;

    procedure TvSeyret;
    procedure SohbetEt;
    procedure OrucunuAc;
  end;

  TAnneThread = class(TEvAhalisiThread)
  private
    fMessage : String;

    procedure Soyle;
  protected
    procedure Execute; override;
  public
    procedure YemekleriIsit;
    procedure Elhamdulillah;
  end;

  TBabaThread = class(TEvAhalisiThread)
  private
    fMessage : String;

    procedure Soyle;
  protected
    procedure Execute; override;
  public
    procedure SofrayiKur;
    procedure Elhamdulillah;
  end;

  TCocukThread = class(TEvAhalisiThread)
  private
    fMessage : String;

    procedure Soyle;
  protected
    procedure Execute; override;
  public
    procedure PideAl;
    procedure Elhamdulillah;
  end;

  TMisafirlerThread = class(TEvAhalisiThread)
  private
    fMessage : String;

    procedure Soyle;
  protected
    procedure Execute; override;
  public
    procedure Elhamdulillah;
  end;

  TZamanThread = class(TThread)
  private
    fIftarSaati : TTime;

    bAnneYemekleriIsit : Boolean;
    bPideAlmayaGit      : Boolean;
    bEzanOkundu	      : Boolean;

    procedure EzanOkundu;
  protected
    procedure Execute; override;
  public
    constructor Create(const IftarSaati : TTime);
  end;

var
  Form1: TForm1;

  Event_AnneYemekleriIsit,
  Event_CocukPideAlmayaGit,
  Event_BabaSofrayiKur,
  Event_EzanOkundu	: THandle;
...
...

Yukarıdaki kod tanımlarımızda TAnneThread, TBabaThread, TCocukThread, TMisafirlerThread ve TZamanThread isimli sınıfları görüyorsunuz. Bu sınıflar birbirleri ile etkileşimde olan sınıflardır. Ve herbirisi TZamanThread’in vereceği komutlar ile kendilerine has görevlerini yapacaklardır. Şimdi arzu ederseniz kodumuza devam edelim, bakalım nasıl bir iletişim halindeler;

procedure TForm1.FormCreate(Sender: TObject);
var
  Error : DWord;
begin
  Event_AnneYemekleriIsit := OpenEvent(EVENT_ALL_ACCESS, false, PAnsiChar('Event Anne Yemekleri Isit'));
  if Event_AnneYemekleriIsit = 0 then
  begin
    Memo1.Lines.Add('Event bulunamadı, oluşturulacak.');
    Event_AnneYemekleriIsit := CreateEvent(nil, false, false, PAnsiChar('Event Anne Yemekleri Isit'));
    Error := GetLastError();

    if Error = ERROR_INVALID_HANDLE then
    	Memo1.Lines.Add('Event Anne Yemekleri Isit ismi daha önce event harici başka bir senkronizasyon nesnesinde kullanılmış.!');
  end
  else Memo1.Lines.Add('Event bulundu ve OpenEvent ile açıldı.');

  Event_BabaSofrayiKur := OpenEvent(EVENT_ALL_ACCESS, false, PAnsiChar('Event Baba Sofrayı Kur'));
  if Event_BabaSofrayiKur = 0 then
  begin
    Memo1.Lines.Add('Event bulunamadı, oluşturulacak.');
    Event_BabaSofrayiKur := CreateEvent(nil, false, false, PAnsiChar('Event Baba Sofrayı Kur'));
    Error := GetLastError();

    if Error = ERROR_INVALID_HANDLE then
    	Memo1.Lines.Add('Event Baba Sofrayı Kur ismi daha önce event harici başka bir senkronizasyon nesnesinde kullanılmış.!');
  end
  else Memo1.Lines.Add('Event bulundu ve OpenEvent ile açıldı.');

  Event_CocukPideAlmayaGit := OpenEvent(EVENT_ALL_ACCESS, false, PAnsiChar('Event Cocuk Pide Almaya Git'));
  if Event_CocukPideAlmayaGit = 0 then
  begin
    Memo1.Lines.Add('Event bulunamadı, oluşturulacak.');
    Event_CocukPideAlmayaGit := CreateEvent(nil, false, false, PAnsiChar('Event Cocuk Pide Almaya Git'));
    Error := GetLastError();

    if Error = ERROR_INVALID_HANDLE then
    	Memo1.Lines.Add('Event Cocuk Pide Almaya Git ismi daha önce event harici başka bir senkronizasyon nesnesinde kullanılmış.!');
  end
  else Memo1.Lines.Add('Event bulundu ve OpenEvent ile açıldı.');

  Event_EzanOkundu := OpenEvent(EVENT_ALL_ACCESS, false, PAnsiChar('Event Ezan Okundu'));
  if Event_EzanOkundu = 0 then
  begin
    Memo1.Lines.Add('Event bulunamadı, oluşturulacak.');
    Event_EzanOkundu := CreateEvent(nil, true, false, PAnsiChar('Event Ezan Okundu')); // Buraya dikkat, ikinci parametre True.!
    Error := GetLastError();

    if Error = ERROR_INVALID_HANDLE then
    	Memo1.Lines.Add('Event Ezan Okundu ismi daha önce event harici başka bir senkronizasyon nesnesinde kullanılmış.!');
  end
  else Memo1.Lines.Add('Event bulundu ve OpenEvent ile açıldı.');
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  CloseHandle(Event_AnneYemekleriIsit);
  CloseHandle(Event_BabaSofrayiKur);
  CloseHandle(Event_CocukPideAlmayaGit);
  CloseHandle(Event_EzanOkundu);
end;

{ TEvAhalisiThread }

constructor TEvAhalisiThread.Create;
begin
  inherited Create(true);
  FreeOnTerminate := true;

  Resume;
end;

procedure TEvAhalisiThread.OrucunuAc;
begin
  // Orucumuzu açalım..
end;

procedure TEvAhalisiThread.SohbetEt;
begin
  // Ahali ile sohbet et..
end;

procedure TEvAhalisiThread.TvSeyret;
begin
  // Arada TV seyret..
end;

{ TAnneThread }
procedure TAnneThread.Soyle;
begin
  form1.Memo1.Lines.Add(fMessage);
end;

procedure TAnneThread.Elhamdulillah;
begin
  fMessage := 'Anne: Afiyet olsun.';

  Synchronize(Soyle);
end;

procedure TAnneThread.Execute;
begin
  inherited;

  while not Terminated do
  begin
    TvSeyret;
    SohbetEt;

    WaitForSingleObject(Event_AnneYemekleriIsit, INFINITE);
    YemekleriIsit;

    WaitForSingleObject(Event_EzanOkundu, INFINITE);
    OrucunuAc;

    Sleep(400);
    Elhamdulillah;

    Terminate;
  end;
end;

procedure TAnneThread.YemekleriIsit;
begin
  fMessage := 'Anne: Yemekleri ısıtmaya başladım.:' + TimeToStr(Time);
  Synchronize(Soyle);

  SetEvent(Event_BabaSofrayiKur); // Gerçek hayatta yemekleri ısıtmaya başlamış olan anne, babaya da sofrayı kurması yönünde ricada bulunur.
end;

{ TBabaThread }
procedure TBabaThread.Soyle;
begin
  form1.Memo1.Lines.Add(fMessage);
end;

procedure TBabaThread.Elhamdulillah;
begin
  fMessage := 'Baba: Hanım eline sağlık.';

  Synchronize(Soyle);
end;

procedure TBabaThread.Execute;
begin
  inherited;

  while not Terminated do
  begin
    TvSeyret;
    SohbetEt;

    WaitForSingleObject(Event_BabaSofrayiKur, INFINITE);
    SofrayiKur;

    WaitForSingleObject(Event_EzanOkundu, INFINITE);
    OrucunuAc;

    Sleep(100);
    Elhamdulillah;

    Terminate;
  end;
end;

procedure TBabaThread.SofrayiKur;
begin
  fMessage := 'Baba: Bende sofrayı kurmaya başladım:' + TimeToStr(Time);

  Synchronize(Soyle);
end;

{ TCocukThread }
procedure TCocukThread.Soyle;
begin
  form1.Memo1.Lines.Add(fMessage);
end;

procedure TCocukThread.Elhamdulillah;
begin
  fMessage := 'Çocuk: Anneciğim ellerine sağlık';

  Synchronize(Soyle);
end;

procedure TCocukThread.Execute;
begin
  inherited;

  while not Terminated do
  begin
    TvSeyret;
    SohbetEt;

    WaitForSingleObject(Event_CocukPideAlmayaGit, INFINITE);
    PideAl;

    WaitForSingleObject(Event_EzanOkundu, INFINITE);
    OrucunuAc;

    Sleep(200);
    Elhamdulillah;

    Terminate;
  end;
end;

procedure TCocukThread.PideAl;
begin
  fMessage := 'Çocuk: Anne ben pide almaya gidiyorum.:' + TimeToStr(Time);

  Synchronize(Soyle);
end;

{ TMisafirlerThread }
procedure TMisafirlerThread.Soyle;
begin
  form1.Memo1.Lines.Add(fMessage);
end;

procedure TMisafirlerThread.Elhamdulillah;
begin
  fMessage := 'Misafir: Yenge ellerine sağlık nefis olmuş';

  Synchronize(Soyle);
end;

procedure TMisafirlerThread.Execute;
begin
  inherited;

  while not Terminated do
  begin
    TvSeyret;
    SohbetEt;

    WaitForSingleObject(Event_EzanOkundu, INFINITE);
    OrucunuAc;

    Sleep(300);
    Elhamdulillah;

    Terminate;
  end;
end;

{ TZamanThread }
procedure TZamanThread.EzanOkundu;
begin
  form1.Memo1.Lines.Add('Ezan okundu:' + TimeToStr(Time));
end;

constructor TZamanThread.Create(const IftarSaati: TTime);
begin
  inherited Create(true); // Bekler vaziyette oluştur..
  FreeOnTerminate := true;

  fIftarSaati := IftarSaati;

  bAnneYemekleriIsit := false;
  bPideAlmayaGit      := false;
  bEzanOkundu	    := false;

  Resume;
end;

procedure TZamanThread.Execute;
var
  CurrentTime : TTime;
begin
  inherited;

  while not Terminated do
  begin
    CurrentTime := Time;

    if (IncMinute(CurrentTime, 30) >= fIftarSaati) and (not bAnneYemekleriIsit) then // O anki zamana 30 dk eklenince iftar zamanını geçiyor ise..
    begin
      SetEvent(Event_AnneYemekleriIsit);
      bAnneYemekleriIsit := true;
    end;

    If (IncMinute(CurrentTime, 5) >= fIftarSaati) and (not bPideAlmayaGit) then
    begin
      SetEvent(Event_CocukPideAlmayaGit);
      bPideAlmayaGit := true;
    end;

    if CurrentTime >= fIftarSaati then
    begin
      SetEvent(Event_EzanOkundu);
      bEzanOkundu := true;
      Synchronize(EzanOkundu);
    end;

    if not bEzanOkundu
    then Sleep(1000) // 1 saniyede bir kontrol edelim..
    else Terminate;
  end;
end;

procedure TForm1.btnEventClick(Sender: TObject);
var
  thrdZaman 	: TZamanThread;
  thrdAnne		: TAnneThread;
  thrdBaba		: TBabaThread;
  thrdCocuk		: TCocukThread;
  thrdMisafir        : TMisafirlerThread;
begin
  // edtIftarSaati TEdit türünde bir component'tir ve içine girilen değer; 18:30:00 gibidir..
  thrdZaman 	:= TZamanThread.Create(StrToTime(edtIftarSaati.Text));

  thrdAnne		:= TAnneThread.Create;
  thrdBaba		:= TBabaThread.Create;
  thrdCocuk 	:= TCocukThread.Create;
  thrdMisafir        := TMisafirlerThread.Create;
end;

Yukarıdaki örneğimizde oluşturulan 5 ayrı thread gözlemliyorsunuz. Bu thread’lerden TZamanThread herşeyi kontrol eden thread’imizdir. Diğer thread’ler tanımlı olan event senkronizasyon nesnelerini bekleyerek kendilerine has işlemleri gerçekleştirirler. TZamanThread sınıfımızın Execute metoduna baktığımızda, kendisine geçilen iftar saati bilgisini sürekli kontrol ettiğini görürürüz. İftar saatine yarım saat kalması durumunda, SetEvent API’si yardımı ile “Event_AnneYemekleriIsit” event değişkenimizi tetiklemiş olur(Kapıyı açar).

Bu tetiklemeye kadar TAnneThread, Execute metodu içindeki WaitForSingleObject(Event_AnneYemekleriIsit, INFINITE); satırı vasıtası ile bekler durumdadır. Event nesnesinin set edilmesine müteakip, bu satırın altına geçebilmiştir(Yemekleri ısıtmaya başlamıştır). Yemekleri ısıtmasının bitmesinden sonra WaitForSingleObject(Event_EzanOkundu, INFINITE); kodu ile ezanın okunmasını beklemeye başlamıştır.

TBabaThread ise Execute metodunda, “Event_BabaSofrayiKur” isimli senkronizasyon nesnesini beklemektedir. TAnneThread.YemekleriIsit metodu içindeki SetEvent ile baba thread’de beklemeyi bırakmış ve sofrayı kurma görevine başlamıştır. Ardından o da tıpkı anne thread’de olduğu gibi ezanın okunmasını bekleyecektir.

TZamanThread iftar’a 5 dakika kala, “Event_CocukPideAlmayaGit” isimli senkronizasyon nesnesini tetikleyecektir. Bu sayede TCocukThread beklemekte olduğu emri alır ve pide almak üzere yola koyulur. Pideleri alıp geri geldiğinde o da diğerleri gibi ezanın okunması için beklemeye başlar.

Bu arada TMisafirThread’imizin Execute metodunda kendisine biçilmiş herhangi bir görevi olmadığını, sadece ezanın okunmasını beklediğini görebilirsiniz. Biz Türk milletinin hasletinde olduğu gibi misafirperverliğimiz sanal dünyada thread’ler içinde dahi devam etmelidir :)

Nihayet ezan vakti geldiğinde TZamanThread’imiz SetEvent(Event_EzanOkundu); API’si yardımı ile event nesnesini tetikleyecek ve bu event’i bekleyen anne, baba, çocuk ve misafir thread’ler bir sonraki kod bloğuna yani OrucunuAc‘a geçebileceklerdir. Ardından zaman thread’imiz Terminate kodunu çağırarak TThread sınıfının Terminated property’sinin true olmasını sağlayacak ve sonsuz döngüden çıkacak, yani threadimiz sonlanacaktır.

Yukarıdaki kod örneğimizde “Event_EzanOkundu” olayının CreateEvent ile oluşturulması koduna bir dikkat ! uyarısı yazmıştım. Hatırlarsanız CreateEvent’in ikinci parametresi bizim event senkronizasyon nesnesinin açık/kapalı olma durumunu otomatik’mi yoksa manuel’mi yapacağımıza karar veren parametre idi. Biz ezan okunması için manuel ayarı seçtik. Bunun sebebi, bu event senkronizasyon nesnesini bekleyen birden fazla thread’in olması ve bizim bu thread’lerin hepsinin birden bekler durumdan kurtulmasını istememizdi.

Eğer parametremizi true değilde false olarak geçse idik, SetEvent ile tetiklenen ezan senkronizasyon nesnesi, tetiklenir tetiklenmez anında tekrar kapalı duruma geçecekti ve dolayısı ile sadece bir thread bekler durumdan kurtulabilecek diğerleri hâla bekler durumda kalacaktı. Halk dili ile, sadece bir kişi orucunu açacak, diğerleri ona bakacak ve kıyamet bundan kopacaktı ;)

Bizde kıyametin kopmasını istemediğimiz için event senkronizasyon nesnesinin açık/kapalı olması durumunu otomatiğe bağlamadık, biz kontrol edeceğiz dedik. Ve açık duruma geldikten sonra, herhangi bir yerde ResetEvent metodunu çağırmadığımız için durum açık olarak kaldı.

Örneğimizi denediğinizde, göreceğiniz çıktı;

  • Anne: Yemekleri ısıtmaya başladım.
  • Baba: Bende sofrayı kurmaya başladım.
  • Çocuk: Anne ben pide almaya gidiyorum.
  • Ezan okundu.
  • Baba: Hanım eline sağlık.
  • Çocuk: Anneciğim ellerine sağlık
  • Misafir: Yenge ellerine sağlık nefis olmuş
  • Anne: Afiyet olsun.
  • gibi olacaktır. Gördüğünüz gibi, Event senkronizasyon mekanizması belirli bir olayı beklemeye dayalıdır. Olayın gerçeklenme koşulunu sizler yönetirsiniz. Kimi zaman bu bir tuşa basma ile gerçekleşir, kimi zaman bir button yada check box’a bastırırsınız yada bizim örneğimizde olduğu gibi bir TZamanThread ile olayları kontrol edersiniz. Bu tamamen sizin ihtiyaçlarınız ile doğru orantılıdır.

    Event senkronizasyon mekanizmasını az çok anlattıktan sonra, bir diğer mekanizmamız olan Waitable Timer kavramına göz gezdirebiliriz. Adından da anlaşılabileceği üzere bu bir timer senkronizasyon nesnedir. Ancak, normal timer’lar gibi değildir. Normal timer olaylarında, işletim sisteminin bir pencereye ihtiyacı vardır. Bu pencereye belirli periotlarda WM_TIMER mesajları gönderilir. Oysa bu mekanizma için herhangi bir penceresel denetime ihtiyaç yoktur ve timer nesnelerinden çok daha hassastır. İşletim sistemindeki timer nesneleri mesaj tabanlı oldukları için bir pencereye ihtiyaç duyarlar ve hassasiyetleri yaklaşık olarak 40-50 milisaniyeler civarındadır. Daha kısa zaman aralıklarına sahip işlemler yapmayı arzu ediyor iseniz o zaman WaitableTimer’ları kullanabilirsiniz. Çünkü Waitable Timer’lar 100 nano saniye hassasiyete sahiptir.

    Özet ile diğer senkronizasyon mekanizmalarında olduğu gibi çalışırlar. Sizin vereceğiniz zaman dilimi geçildiğinde senkronizasyon nesnesini açar yada kapatırlar. Hatırlayacağınız üzere yukarıdaki örneğimizde biz bu işi TZamanThread ile yapıyorduk. Şimdi yukarıdaki örneğimizi, Waitable Timer senkronizasyon nesnesine göre yeniden yazacağız. Ancak öncelikle herzamanki gibi tanımlarımıza bir göz gezdirelim:

    HANDLE CreateWaitableTimer(
        LPSECURITY_ATTRIBUTES lpTimerAttributes,
        BOOL bManualReset,	
        LPCTSTR lpTimerName
       );	
    
    HANDLE OpenWaitableTimer(
        DWORD dwDesiredAccess,
        BOOL bInheritHandle,
        LPCTSTR lpTimerName
       );	
    
    BOOL SetWaitableTimer(
        HANDLE hTimer,	
        const LARGE_INTEGER *pDueTime,
        LONG lPeriod,
        PTIMERAPCROUTINE pfnCompletionRoutine,
        LPVOID lpArgToCompletionRoutine,
        BOOL fResume
       );
    
    function CreateWaitableTimer(lpTimerAttributes: PSecurityAttributes; bManualReset: BOOL; lpTimerName: PChar): THandle; stdcall;
    
    function OpenWaitableTimer(dwDesiredAccess: DWORD; bInheritHandle: BOOL;
      lpTimerName: PChar): THandle; stdcall;
    
    function SetWaitableTimer(hTimer: THandle; var lpDueTime: TLargeInteger;
      lPeriod: Longint; pfnCompletionRoutine: TFNTimerAPCRoutine;
      lpArgToCompletionRoutine: Pointer; fResume: BOOL): BOOL; stdcall;
    

    Görüldüğü gibi bu senkronizasyon mekanizmasının metodlarının biraz daha farklı parametreleri var. CreateWaitableTimer yada OpenWaitableTimer metodları diğer senkronizasyon mekanizmalarında olduğu gibi benzerlikler arz ediyor. Lâkin, SetWaitableTimer şimdiye dek gördüklerimizden farklı bir yapıya sahip. Waitable Timer’lar tetikleme zamanı için geçeceğiniz zaman bilgisinin TSystemTime, TFileTime türleri arasındaki geçişleri ile ilgili bazı tür dönüşümleri gerektirirler. Gelin kullanımlarına geçmeden evvel ihtiyaç duyacağımız parametre tiplerine yakından bakalım;

      PLargeInteger = ^TLargeInteger;
      _LARGE_INTEGER = record
        case Integer of
        0: (
          LowPart: DWORD;
          HighPart: Longint);
        1: (
          QuadPart: LONGLONG);
      end;
      TLargeInteger = Int64;
      LARGE_INTEGER = _LARGE_INTEGER;
    
      PSystemTime = ^TSystemTime;
      _SYSTEMTIME = record
        wYear: Word;
        wMonth: Word;
        wDayOfWeek: Word;
        wDay: Word;
        wHour: Word;
        wMinute: Word;
        wSecond: Word;
        wMilliseconds: Word;
      end;
      TSystemTime = _SYSTEMTIME;
      SYSTEMTIME = _SYSTEMTIME;
    
      PFileTime = ^TFileTime;
      _FILETIME = record
        dwLowDateTime: DWORD;
        dwHighDateTime: DWORD;
      end;
      TFileTime = _FILETIME;
      FILETIME = _FILETIME;
    

    Bir Waitable Timer ‘a zaman bilgisini geçebilmemiz için öncelikle TSystemTime türündeki record değişkenimizin içerisine gerekli bilgileri yazmalı, ardından SystemTimeToFileTime API’si ile gerekli dönüşümü sağlamalı, LocalFileTimeToFileTime ile bir dönüşüm daha sağladıktan sonra TLargeInteger türündeki parametremizin yüksek ve düşük word’lerine gereken atamaları yapmalıyız. Karmaşık gibi görünen bu anlatım aslında göründüğü kadar karmaşık değil. Bunu yazacağımız kodda sizlerde gözlemleyebileceksiniz. Ancak kodlara geçmeden evvel teorik bilgileri vermeye devam etmekte fayda olduğu kanaatindeyim.

    SetWaitableTimer’ın üçüncü parametresi Waitable timer’ın ne kadar zamanda bir çalışacağını ifade eder. Biz bu değere 1000 geçerek 1 saniyede bir kontrol etmesini isteyeceğiz. Dördüncü ve beşinci parametreler, waitable timer senkronizasyon mekanizmasının belirtilen zamana eriştiğinde çağırmasını istediğiniz bir metod var ise anlamlı olacaktır. Dördüncü parametrenin Delphi’deki tanım şekli aşağıdaki gibi olacaktır:

    procedure (lpArgToCompletionRoutine: Pointer;
        dwTimerLowValue, dwTimerHighValue: DWORD); stdcall;
    

    Beşinci parametre ise herhangi bir geçerli pointer adresi olabilir. Ancak biz örneğimizde bu iki parametreyi de nil olarak geçeceğiz. Çünkü APC(Asynchronous Procedure Call) adı verilen tekniğin detaylarına girmek istemiyorum, bunu sizin araştırmalarınıza bırakıyorum. Yalnız bir ipucu olarak, APC çağrılarının uygulama kuyruğuna eklendiğini ve bu çağrıları yakalayabilmek için SetWaitableTimer API’sini çağıran thread’de SleepEx(INFINITE, true) ile beklemeniz gerektiğini söyleyebilirim. Bu beklemeyi yapmadığınız zaman APC çağrılarını yakalayamazsınız.

    Örneğimize geçmeden evvel OpenWaitableTimer için gereken bazı sabitlerin Delphi’de tanımlı olmadıklarını hatırlatmak ve bu tanımları sizlerle paylaşmak isterim;

    const
     TIMER_QUERY_STATE  = $0001;
     TIMER_MODIFY_STATE = $0002;
    
     TIMER_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE or TIMER_QUERY_STATE or TIMER_MODIFY_STATE;
    

    Evet, tüm bu sıkıcı teorisel bilgilerin ardından isterseniz iftar ile ilgili örneğimizi, Waitable Timer senkronizasyon mekanizmasını kullanarak ve ana yapımızı pek de değiştirmeden nasıl kodlayabileceğimize yakinen bakalım:

    type
      TEvAhalisiThread = class(TThread)
      public
        constructor Create;
    
        procedure TvSeyret;
        procedure SohbetEt;
        procedure OrucunuAc;
      end;
    
      TAnneThread = class(TEvAhalisiThread)
      private
        fMessage : String;
        procedure Soyle;
      protected
        procedure Execute; override;
      public
        procedure YemekleriIsit;
        procedure Elhamdulillah;
      end;
    
      TBabaThread = class(TEvAhalisiThread)
      private
        fMessage : String;
        procedure Soyle;
      protected
        procedure Execute; override;
      public
        procedure SofrayiKur;
        procedure Elhamdulillah;
      end;
    
      TCocukThread = class(TEvAhalisiThread)
      private
        fMessage : String;
        procedure Soyle;
      protected
        procedure Execute; override;
      public
        procedure PideAl;
        procedure Elhamdulillah;
      end;
    
      TMisafirlerThread = class(TEvAhalisiThread)
      private
        fMessage : String;
        procedure Soyle;
      protected
        procedure Execute; override;
      public
        procedure Elhamdulillah;
      end;
    
    var
      Form1: TForm1;
    
      Waitable_AnneYemekleriIsit,
      Waitable_CocukPideAlmayaGit,
      Waitable_EzanOkundu		: THandle;
    
    implementation
    
    procedure TForm1.FormCreate(Sender: TObject);
    var
      Error : DWord;
    begin
      Waitable_AnneYemekleriIsit := OpenWaitableTimer(TIMER_ALL_ACCESS, false, PAnsiChar('Waitable Anne Yemekleri Isit'));
      if Waitable_AnneYemekleriIsit = 0 then
      begin
        Memo1.Lines.Add('Waitable Timer bulunamadı, oluşturulacak.');
        Waitable_AnneYemekleriIsit := CreateWaitableTimer(nil, false, PAnsiChar('Waitable Anne Yemekleri Isit'));
        Error := GetLastError();
    
        if Error = ERROR_INVALID_HANDLE then
        	Memo1.Lines.Add('Waitable Anne Yemekleri Isit ismi daha önce Waitable Timer harici başka bir senkronizasyon nesnesinde kullanılmış.!');
      end
      else Memo1.Lines.Add('Waitable Timer bulundu ve OpenWaitableTimer ile açıldı.');
    
      Waitable_CocukPideAlmayaGit := OpenWaitableTimer(TIMER_ALL_ACCESS, false, PAnsiChar('Waitable Cocuk Pide Almaya Git'));
      if Waitable_CocukPideAlmayaGit = 0 then
      begin
        Memo1.Lines.Add('Waitable Timer bulunamadı, oluşturulacak.');
        Waitable_CocukPideAlmayaGit := CreateWaitableTimer(nil, false, PAnsiChar('Waitable Cocuk Pide Almaya Git'));
        Error := GetLastError();
    
        if Error = ERROR_INVALID_HANDLE then
        	Memo1.Lines.Add('Waitable Cocuk Pide Almaya Git ismi daha önce Waitable Timer harici başka bir senkronizasyon nesnesinde kullanılmış.!');
      end
      else Memo1.Lines.Add('Waitable Timer bulundu ve OpenWaitableTimer ile açıldı.');
    
      Waitable_EzanOkundu := OpenWaitableTimer(TIMER_ALL_ACCESS, false, PAnsiChar('Waitable Ezan Okundu'));
      if Waitable_EzanOkundu = 0 then
      begin
        Memo1.Lines.Add('Waitable Timer bulunamadı, oluşturulacak.');
        Waitable_EzanOkundu := CreateWaitableTimer(nil, true, PAnsiChar('Waitable Ezan Okundu')); // İkinci parametreye dikkat, eventlerdeki açıklamamızı hatırlayınız.
        Error := GetLastError();
    
        if Error = ERROR_INVALID_HANDLE then
        	Memo1.Lines.Add('Waitable Ezan Okundu ismi daha önce Waitable Timer harici başka bir senkronizasyon nesnesinde kullanılmış.!');
      end
      else Memo1.Lines.Add('Waitable Timer bulundu ve OpenWaitableTimer ile açıldı.');
    end;
    
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      CloseHandle(Waitable_AnneYemekleriIsit);
      CloseHandle(Waitable_CocukPideAlmayaGit);
      CloseHandle(Waitable_EzanOkundu);
    end;
    
    { TEvAhalisiThread }
    
    constructor TEvAhalisiThread.Create;
    begin
      inherited Create(true);
      FreeOnTerminate := true;
    
      Resume;
    end;
    
    procedure TEvAhalisiThread.OrucunuAc;
    begin
      // Orucumuzu açalım..
    end;
    
    procedure TEvAhalisiThread.SohbetEt;
    begin
      // Ahali ile sohbet et..
    end;
    
    procedure TEvAhalisiThread.TvSeyret;
    begin
      // Arada TV seyret..
    end;
    
    { TAnneThread }
    
    procedure TAnneThread.Soyle;
    begin
      form1.Memo1.Lines.Add(fMessage);
    end;
    
    procedure TAnneThread.Elhamdulillah;
    begin
      fMessage := 'Anne: Afiyet olsun.';
    
      Synchronize(Soyle);
    end;
    
    procedure TAnneThread.Execute;
    begin
      inherited;
    
      while not Terminated do
      begin
        TvSeyret;
        SohbetEt;
    
        WaitForSingleObject(Waitable_AnneYemekleriIsit, INFINITE);
        YemekleriIsit;
    
        WaitForSingleObject(Waitable_EzanOkundu, INFINITE);
        OrucunuAc;
    
        Sleep(400);
        Elhamdulillah;
    
        Terminate;
      end;
    end;
    
    procedure TAnneThread.YemekleriIsit;
    begin
      fMessage := 'Anne: Yemekleri ısıtmaya başladım.:' + TimeToStr(Time);
      Synchronize(Soyle);
    end;
    
    { TBabaThread }
    
    procedure TBabaThread.Soyle;
    begin
      form1.Memo1.Lines.Add(fMessage);
    end;
    
    procedure TBabaThread.Elhamdulillah;
    begin
      fMessage := 'Baba: Hanım eline sağlık.';
    
      Synchronize(Soyle);
    end;
    
    procedure TBabaThread.Execute;
    begin
      inherited;
    
      while not Terminated do
      begin
        TvSeyret;
        SohbetEt;
    
        WaitForSingleObject(Waitable_AnneYemekleriIsit, INFINITE);
        SofrayiKur;
    
        WaitForSingleObject(Waitable_EzanOkundu, INFINITE);
        OrucunuAc;
    
        Sleep(100);
        Elhamdulillah;
    
        Terminate;
      end;
    end;
    
    procedure TBabaThread.SofrayiKur;
    begin
      fMessage := 'Baba: Bende sofrayı kurmaya başladım:' + TimeToStr(Time);
    
      Synchronize(Soyle);
    end;
    
    { TCocukThread }
    
    procedure TCocukThread.Soyle;
    begin
      form1.Memo1.Lines.Add(fMessage);
    end;
    
    procedure TCocukThread.Elhamdulillah;
    begin
      fMessage := 'Çocuk: Anneciğim ellerine sağlık';
    
      Synchronize(Soyle);
    end;
    
    procedure TCocukThread.Execute;
    begin
      inherited;
    
      while not Terminated do
      begin
        TvSeyret;
        SohbetEt;
    
        WaitForSingleObject(Waitable_CocukPideAlmayaGit, INFINITE);
        PideAl;
    
        WaitForSingleObject(Waitable_EzanOkundu, INFINITE);
        OrucunuAc;
    
        Sleep(200);
        Elhamdulillah;
    
        Terminate;
      end;
    end;
    
    procedure TCocukThread.PideAl;
    begin
      fMessage := 'Çocuk: Anne ben pide almaya gidiyorum.:' + TimeToStr(Time);
    
      Synchronize(Soyle);
    end;
    
    { TMisafirlerThread }
    
    procedure TMisafirlerThread.Soyle;
    begin
      form1.Memo1.Lines.Add(fMessage);
    end;
    
    procedure TMisafirlerThread.Elhamdulillah;
    begin
      fMessage := 'Misafir: Yenge ellerine sağlık nefis olmuş';
    
      Synchronize(Soyle);
    end;
    
    procedure TMisafirlerThread.Execute;
    begin
      inherited;
    
      while not Terminated do
      begin
        TvSeyret;
        SohbetEt;
    
        WaitForSingleObject(Waitable_EzanOkundu, INFINITE);
        OrucunuAc;
    
        Sleep(300);
        Elhamdulillah;
    
        Terminate;
      end;
    end;
    
    procedure TForm1.btnWaitableTimerClick(Sender: TObject);
    type
      TInt64Rec = record
        Lo : DWord;
        Hi : DWord;
      end;
    var
      SysTime : TSystemTime;
    
      ft,
      ftResult: TFileTime;
      li : Int64;
    
      IftarZamani : TTime;
      wGun,
      wAy,
      wYil,
      wSaat,
      wDakika,
      wSaniye,
      wMiliSaniye : Word;
    
      thrdAnne		: TAnneThread;
      thrdBaba		: TBabaThread;
      thrdCocuk		: TCocukThread;
      thrdMisafir        : TMisafirlerThread;
    begin
      IftarZamani := StrToTime(edtIftarSaati.Text);
    
      DecodeDate(Date, wYil, wAy, wGun);
      DecodeTime(IftarZamani, wSaat, wDakika, wSaniye, wMiliSaniye);
    
      SysTime.wYear 		:= wYil;
      SysTime.wMonth 	:= wAy;
      SysTime.wDayOfWeek := 0;
      SysTime.wDay 		:= wGun;
      SysTime.wHour 		:= wSaat;
      SysTime.wMinute 	:= wDakika;
      SysTime.wSecond     	:= wSaniye;
      SysTime.wMilliseconds := 0;
    
      SystemTimeToFileTime(SysTime, ft);
      LocalFileTimeToFileTime(ft, ftResult);
    
      TInt64Rec(li).Lo := ftResult.dwLowDateTime;
      TInt64Rec(li).Hi := ftResult.dwHighDateTime;
    
      SetWaitableTimer(Waitable_EzanOkundu, li, 1000, nil, nil, false);
    
      DecodeTime(IncMinute(IftarZamani, -30), wSaat, wDakika, wSaniye, wMiliSaniye); // Annenin yemekleri ısıtacağı zaman..
      SysTime.wHour     := wSaat;
      SysTime.wMinute  := wDakika;
      SysTime.wSecond := wSaniye;
    
      SystemTimeToFileTime(SysTime, ft);
      LocalFileTimeToFileTime(ft, ftResult);
    
      TInt64Rec(li).Lo := ftResult.dwLowDateTime;
      TInt64Rec(li).Hi := ftResult.dwHighDateTime;
      SetWaitableTimer(Waitable_AnneYemekleriIsit, li, 1000, nil, nil, false);
    
      DecodeTime(IncMinute(IftarZamani, -5), wSaat, wDakika, wSaniye, wMiliSaniye); // Çocuğun pide almaya gideceği zaman..
      SysTime.wHour     := wSaat;
      SysTime.wMinute  := wDakika;
      SysTime.wSecond := wSaniye;
    
      SystemTimeToFileTime(SysTime, ft);
      LocalFileTimeToFileTime(ft, ftResult);
    
      TInt64Rec(li).Lo := ftResult.dwLowDateTime;
      TInt64Rec(li).Hi := ftResult.dwHighDateTime;
      SetWaitableTimer(Waitable_CocukPideAlmayaGit, li, 1000, nil, nil, false);
    
      thrdAnne		:= TAnneThread.Create;
      thrdBaba		:= TBabaThread.Create;
      thrdCocuk 	:= TCocukThread.Create;
      thrdMisafir        := TMisafirlerThread.Create;
    end;
    

    Evet hepsi bu kadar. Gördüğünüz gibi diğerlerine nazaran tek zorluğu SetWaitableTimer API’sinin parametrelerinde yaşadık. Geri kalan kullanım diğer senkronizasyon nesnelerinde izah ettiğimiz gibi gerçekleşiyor. Bu örneğimizde zamanlamayı kontrol etmek maksadı ile daha evvel kullandığımız TZamanThread sınıfına ihtiyacımız olmadı. Çünkü zamanlama bilgisini Waitable Timer’lar ile sağlıyoruz. Belirttiğimiz zaman geldiğinde, Waitable Timer senkronizasyon nesnesi otomatikman açık duruma geçecek, dolayısı ile bu senkronizasyon nesnelerini WaitForSingleObject yada WaitForMultipleObjects ile bekleyen thread’lerimiz çalışmalarına kaldıkları yerden devam edebilme şansını elde edebileceklerdir.

    Bu konularla ilgili herhangi bir görüş, düşünce yada öneriniz var ise yorum olarak paylaşmanızdan bilhassa memnuniyet duyarım. Bundan sonra yazacağım küçük makale bir thread’in TerminateThread kullanılmadan düzgün bir şekilde nasıl kapatılacağına yönelik olacaktır.

    Bir sonraki makalede görüşmek ümidi ile hoşçakalın..

    “Derinlemesine Threading..(2)” için 10 Yorum

    1. Veli BOZATLI diyor ki:

      Böyle önemli bir konu, böyle güncel bir örnekleme ile ancak bu kadar güzel anlatılabilirdi !
      Çok güzel bir kandil hediyesi olmuş :)
      Sağolasın Tuğrul Hocam…

    2. Olcay DAĞLI diyor ki:

      Oldukça öğretici ve espirili bir anlatım olmuş hocam. Benim tek önerim, o kadar “Event” anlatımından sonra “Evet” kelimesini hiç kullanmasaydın olabilir sanırım :) Zira evet leri ivınt olarak okumak oldukça komik oluyor :D
      Ellerine sağlık… ;)

    3. Tuğrul HELVACI diyor ki:

      Sizler sağolun arkadaşlar, bu konular anlaşılması biraz zor konular olduğu için anlatması da biraz zor oluyor. Mümkün mertebe anlaşılır örneklere başvurmak icap ediyor. Örneğimi beğendi iseniz yeterince açık anlatabilmişim demektir.

    4. ahmet meriç diyor ki:

      Tugrul Bey Merhaba

      Thread lerle ilgili makalelerinizi okudum.Çok teşekkür ederim.Benim için çok faydalı bilgiler içeriyor.
      Konu ile ilgili bir sorunumuzu sizinle paylaşmak istiyoruz.
      Paralel Çalışan Multithread bir uygulamada threadler içinde new() komutu ile pointerlar oıluşturmamız gerekiyor ancak bu aşamada threadler birbirini bekliyor ve normalin(singlethread) çok altında bir zamanlama ile işlem tamamlanıyor.Bu sorunu nasıl aşabiliriz.

      • Tuğrul HELVACI diyor ki:

        Bir thread içinde yapılan memory allocation’ın bir diğer thread’i bekletmesi yada duraklatması söz konusu değildir. Sorun memory allocation olmamalı kanaatimce. Detaylandırabilirseniz daha fazla yardımcı olabilirim sanırım.

    5. ahmet meriç diyor ki:

      Tuğrul Bey; öncelikle , ilginiz için çok Teşekkür Ediyoruz.
      Sorunumuzun delphi kodu aşagıdaki gibidir.
      type th=class(tthread)
      private
      protected
      procedure execute;override;
      public
      constructor create;
      end;

      type
      TForm1 = class(TForm)
      SpeedButton1: TSpeedButton;
      procedure SpeedButton1Click(Sender: TObject);
      procedure FormCreate(Sender: TObject);
      private
      { Private declarations }
      public
      { Public declarations }
      end;

      var
      Form1: TForm1;
      t1:array of th;

      implementation

      {$R *.dfm}
      procedure th.execute;
      var i,j,k:integer;
      x:pinteger;
      begin
      for i:=0 to 1000000000 do begin
      j:=round(sqrt(i));
      new(x);dispose(x);//SORUNLU SATIR
      end;

      Terminate;
      end;
      constructor th.create;
      begin
      inherited create(true);
      end;

      procedure TForm1.SpeedButton1Click(Sender: TObject);
      var i:integer;
      begin
      for i:=0 to high(t1) do begin
      t1[i]:=th.create
      end;

      for i:=0 to high(t1) do begin
      t1[i].Resume;
      end;

      for i:=0 to high(t1) do begin
      t1[i].WaitFor;
      end;

      for i:=0 to high(t1) do begin
      t1[i].Free;
      end;

      end;

      procedure TForm1.FormCreate(Sender: TObject);
      var i:integer;
      begin
      SetLength(t1,4);
      end;

      • Tuğrul HELVACI diyor ki:

        Göndermiş olduğunuz kodda bir aksaklık yok gibi görünüyor. Siz 1 milyar kere mem. allocation yapmışsınız. Pointer’ların memory’de 4 byte yer kapladığı düşünülürse bu da 4 milyar byte’lık bir alan rezerve edip boşalttığınız anlamını taşıyor. Ancak memory allocation işlemine müteakip hemen hafızayı boşalttığınız için bu da bir sorun olmaz. Bütün threadlerinizi Resume ile başlattığınız için, WaitFor çağrımının bitmesi 1 adet thread’in bitmesi kadar zaman alacaktır.

        Örneğin; threadleriniz içinde 5 sn. gibi bir bekleme yapmış olsa idiniz, tüm threadler aynı anda paralel işletileceği için toplam bekleme zamanınız 5 sn + bir kaç milisaniye cinsinden olurdu. Bu bağlamda; ben hâla memory allocation’da ne gibi spesifik bir sorunla karşı karşıyasınız anlayamadım. Sizin örneğinize benzer bir örnek geliştirmiş olmama rağmen; paralel execution’ın normal bir şekilde gerçekleştiğini ve memory allocation/deallocation bloklarının sağlıklı çalıştığını gözlemledim.

        Karşılaştığınız sorun hakkında daha açıklayıcı bir malümat verebilir ve anlamama yardımcı olursanız, mümkün mertebe faydalı olmaya gayret ederim.

    6. ahmet meriç diyor ki:

      Merhaba tuğrul bey;

      Göndermiş oldugum örnek kodda new() ;dispose() satırı
      iptal edildiginde işlemcilerin performası (quad core ) %100 e ulaşıyor
      Sorunlu satır aktif edildiğinde toplam işlemci performası %15 e düşüyor.yani tek çekirdeği bile düzgün kullanamıyor.
      Benim Tesbitim bu konuda şu şekilde;
      memory allaoction yetkisi sadece Mainthread e verilmiş (windows tarafından) dolayısı ile memory bloku üzerinde talebi olan threadler bu hizmeti mainthreadden alıyor. ve hafızayı alan geri dönüyor.
      aynı andaki talepleri ise mainthread in bile kafasını karıştırıyor ve normal tek çekirdek performası bile düşüyor.
      Araştırmalarım neticesi bu konunun sadece bizim sorunumuz olmadığını gördüm.
      Bu yüzden İntel bu konuda bir yazılım geliştirmiş
      Multithread uygulamalar için (ücretli bir yazılım TBB) sanırım onu deneyeceğiz.

    7. geyikben diyor ki:

      Bu Ancak Böyle Anlatılabilirdi. Tebrik ve Teşekkür Ederim.

    8. ABDULLAH KAYAALP diyor ki:

      Tuğrul hocam çok güzel bir makale,elinize,klavyenize sağlık.

    Yorum Yazın