‘API’ Etiket Arşivi

Derinlemesine Threading..(3)

// 3 Nisan 2010 // 13 Yorum » // Delphi, İşletim Sistemi, Win32

Threading ile ilgili bir önceki makelemizde Event ve WaitableTimer senkronizasyon mekanizmalarını anlatmış ve konuya ışık tutabilmesi adına örnekler paylaşmıştık. Makalemizin sonunda ise; bir thread’i normal yollarla durdurabilmek için(TerminateThread API’sini kullanmadan) bir makale yazacağımdan bahsetmiştim. Bu makalemizin konusu verdiğim söz gereği; bir thread’i sonlandırma seçenekleri ile ilgili olacak.

Ancak, sizlerden ricam bu makalede ilerlemeden önce Threading ile ilgili yazılmış olan diğer makaleleride okumanızdır. İlgili makalelere aşağıdaki linklerden erişebilirsiniz:

Nedir bu Thread’lerden çektiğimiz..!
Derinlemesine Threading..(1)
Derinlemesine Threading..(2)

Şimdi tüm bu makaleleri okuduğunuzu ve threading hakkında fikriniz olduğunu varsayarak, bir thread’i neden durdurmak isteyebileceğimizi ve karşımıza ne gibi sorunların çıkabileceğini biraz izah etmeye çalışalım. Bildiğiniz gibi thread’leri genellikle paralel programlama yapabilmek, iş yükünü dağıtabilmek, ana uygulamamızın kilitlenmesine mani olabilmek adına kullanırız. Ve bazen, thread’lerimizin içindeki kodlar uzun süreli ve hatta kullanıcı ile interaktif çalışıyor da olabilir.

Bazı durumlarda, yazdığımız thread’lerin içinde çalışan kod bloklarını sonlandırmak isteriz. Buna sanırım en güzel örnek, veritabanına bağlanıp büyük bir sonuç seti çekmeye çalıştığımız zamanlarda programımızın kullanıcısının isteği ile rapor alımını durdurmak verilebilir.

Pek çok programcı bu gibi durumlarda, çalışan thread’ini durdurabilmek için TThread sınıfının Terminated özelliğini kontrol eder. Terminated’in true olması durumunda thread çalışma kodundan çıkılmasını sağlar. Buna küçük bir örnek verebiliriz;

procedure TMyThread.Execute;
begin
  inherited;

  while not Terminated do
  begin
     //...
     //...
  end;
end;

Yukarıdaki örnek, sıklıkla kullanılan çok genel bir örnektir. Malumunuz olduğu üzere; TThread sınıfının Terminate metodu bir thread’i durdurma işini yapmaz. Terminate metodu; Terminated isimli property’nin True olarak set edilmesini sağlar. Programımız içerisinde herhangi bir yerde TThread sınıfının Terminate metodunun çağrılması yukarıdaki kod örneği için ilgili thread’in sonlanması anlamını taşır.

Buraya kadar anlattıklarımızda bir sıkıntı ve bu makaleye hayat verecek bir neden de yok gibi görünüyor. Ancak; bizler her zaman thread’lerimiz içinde yukarıdaki kısa kod örneğinde olduğu gibi Terminated property’sinden istifade edemeyiz. Bu hususta da kısa bir örnek verip devam etmek sanırım daha açıklayıcı olacaktır:
(more…)

Derinlemesine Threading..(2)

// 20 Temmuz 2009 // 10 Yorum » // 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.
(more…)

Derinlemesine Threading..(1)

// 5 Temmuz 2009 // 31 Yorum » // Delphi, İşletim Sistemi, Win32

Hâla programlama aleminde threading uzak kalınası bir mesele gibi gözlemleniyor. Aslında belli başlı kurallar bilindiğinde bu konunun korkulacak bir konu değil ancak dikkatle ele alınması gereken bir konu olduğu anlaşılacaktır. Genellikle thread dediğimiz mekanizmayı yazdığımız kodların paralel çalışabilmesi için kullanırız. Ancak kullandığımız işletim sistemleri gerçek mânada multi-threaded olmadıkları için hepimizin başına bir ton sorun gelmiştir ve thread’lere lanetler okumuşuzdur. Dolayısı ile onlardan uzak kalmaya mümkün mertebe gayret etmişizdir. Bu makalede amacım, siz değerli okuyucularımı bu mekanizmaya yaklaştırmak, varsa çekincelerinizi bilgim dahilinde gidermeye çalışmak olacaktır.

Thread’lerle düzgün şekilde çalışabilmek için öncelikle çok derin olmasa da orta seviyede işletim sistemi çalışma mekanizmasına aşina olmalıyız. Bu bağlamda işletim sistemleri üzerinde nispeten az bilinen hususlara değinmeye çalışacağım.

İyi bir programcı, üzerinde çalıştığı işletim sistemini tanıyan, onun neleri yapabileceğini ve neleri yapamayacağını bilen programcıdır. Bu öngörü, her zaman düşük seviyeli işletim sistemi bilgilerine müracaat etmeniz gerektiği anlamına gelmez. Yüksek seviye bir dil de kullanabiliyor olabilirsiniz, ancak her zaman olayın arka planında neler olduğunu bilen bir programcı diğerlerine oranla basamaklarca önde olacaktır. Tercih tamamen sizlere aittir. “Çok derin bilgilere girmem, bildiklerim bana yeter, ekmeğimi kazanırım keyfime bakarım” düşüncesinde iseniz, bildiklerinizin size yetmeyeceği gerçeği ile birgün karşılaşacağınızı üzülerek ifade etmek zorunda kalacağım. Her geçen gün sürekli değişen bir bilişim dünyasında kalıcı olabilmeniz; temel seviye bilgilerinizin yüksekliği ile doğrudan orantılıdır.

Yüzyüze karşılaştığım, yada bir şekilde derinlemesine programlama alemi ile ilgili muhabbete girdiğim arkadaşlarıma verdiğim tavsiyeler hep bu yönde olmuştur. Benim kişisel kanım, iyi bir programcının her zaman üzerinde çalıştığı sistem hakkında bilgiye haiz olması üzerinedir. Hemen hemen her programcı, günümüz popüler dillerinde nesneye yönelik programlamayı, pointer’ları ve daha pek çok low level teknolojiyi farkında olarak yada olmayarak kullanmaktadır. Dileğim odur ki, sizler farkında olanların safında olun. Bu farkındalık, sadece şahsınıza değil; şahsınız nezdinde pek çok insana ve hatta ülkenize de faydanız olacağı mânasına gelir.

Blog’umuz genel mânada Delphi üzerine olduğu için vereceğim örnekler Delphi üzerine olacaktır ama bu söylemler programlama ile uğraşan herkesi ilgilendirir. Delphi’ye yeni başlayan birisi dahi olsanız, farkında olmadan pointer’ları kullanıyor, nesneye yönelik programlama dünyasının avantajlarından sonuna kadar istifade ediyor, design pattern denilen popüler programlama konseptlerini programlarınızda kullanıyorsunuz demektir. Delphi, bu derin bilgi gerektiren hususların kullanım zorluklarını, kullanıcılarından gizleme konusunda son derece maharetlidir. Makalemde ilerlemeden evvel, her programcı arkadaşıma Delphi’nin kaynak kodlarında dolaşmasını öneriyorum.
(more…)

49.7 gün sonra GetTickCount !

// 6 Haziran 2009 // 3 Yorum » // Delphi, İşletim Sistemi, Programlama, Win32

Eğer sizlerde projelerinizde zamansal kontroller kullanıyorsanız, iki kod sürecinin arasındaki zaman dilimini hesaplıyorsanız muhtemelen GetTickCount API’sini kullanıyorsunuz demektir. Bu API, işletim sistemi açıldığı zaman sıfır(0)’a eşitlenen ve her milisaniyede bir değeri bir arttırılan işletim sistemine has bir değişkenin değerini bizlere geri verir. Win32 API Help’te tanımı aşağıdaki gibidir:

DWORD GetTickCount(VOID)

Görüldüğü üzere geri dönüş değeri DWord türünde. Delphi’de DWord, LongWord türüne eşitlenmiştir. LongWord ise 32 bit işaretsiz bir tamsayıdır. Yani bu sayının alabileceği değer aralığı; 0..4.294.967.295 dır. Bu değerlerin doğruluğunu aşağıdaki kod ile de kontrol edebilirsiniz:

var
  dw : DWord;
begin
  dw := Low(DWord);
  ShowMessage(InttoStr(dw));

  dw := High(DWord);
  ShowMessage(InttoStr(dw));
end;

Hatırlarsanız eğer, GetTickCount API’sinin her milisaniyede bir işletim sistemi içinde bir yerde bulunan bir değişkenin değerini bir arttırdığını ve bu değeri geri döndürdüğünü söylemiştik. Peki; 4.294.967.295 + 1 milisaniye sonra ne olacak ? İsterseniz bu milisaniyenin kaç güne tekabül ettiğine önce bir bakalım;

  • 4.294.967.295 ms = 4.294.967.295 ms / 1000 = 4.294.967,295 sn.
  • 4294967,295 sn = 4294967,295 sn / 60 = 71.582,78825 dk.
  • 71.582,78825 dk = 71.582,78825 dk / 60 = 1.193,04647083 saat
  • 1.193,04647083 saat = 1.193,04647083 saat / 24 = 49,71 gün
  • Gördüğünüz gibi 49.7 gün sonra GetTickCount API fonksiyonu uygulamamız için tehlike sinyalleri vermeye başlayacaktır. 49.7 günün sonunda işletim sistemindeki değişken maksimum değerine erişecek ve bir sonraki milisaniyede bu değişken otomatikman sıfırlanacaktır. Dolayısı ile yapacağımız zamansal kontroller hatalı neticeler üretecek, belkide beklemediğimiz durumlar ile karşılaşacağız.

    Peki ne yapmalıyız ? Aslında yapılabilecek şeyler sınırlı, ama ben size QueryPerformanceCounter‘a bakmanızı yada Vista işletim sistemi kullanıyorsanız GetTickCount’un 64 bit değer döndüren ve kernel32.dll içinde tanımlanmış ancak Delphi’de implemente edilmemiş GetTickCount64 fonksiyonunu kullanmanızı önerebilirim.

    49.7 gün bir bilgisayar açık kalmaz, benim programım da 49.7 gün sürekli açık kalacaksa patlarsa patlasın derseniz o zaman bu hususlara bakmayabilirsiniz tabii :)

    Saygılar, sevgiler..

    Bir Kiosk ve CreateDesktop macerası..

    // 20 Mayıs 2009 // 36 Yorum » // Delphi, Programlama

    Bu yazıma gecenin bu saatinde başladığıma göre büyük ihtimalle yarın işe geç kalacağım. Ama tamamlandığında da bu konuyu 3 gündür makaleleştirememenin verdiği sıkıntıdan da kurtulmuş olacağım. Sanırım bu kafi derecede haz verir bana.

    Hayatının herhangi bir anında herhangi bir husus hakkında birşeyler karalamış olanlar bilirler ki, bir şeyi biliyor olmak ile anlatabiliyor olmak cidden farklı. Bunlar farklı kaabiliyetler sanıyorum. Ve yine yazmaya gönlü kaymış kişiler bilir ki; bazen bir konuyu layıkı ile anlatamama endişesi “acaba hiç mi yazmasam” düşüncesine hayat verir. Bende böyle gitgeller yaşadım bu makale için ama elimden geldiğince kendi üslubum gereği aklıma gelen tüm detaylara da inerek bir cesaretle ya Allah diyip başlıyorum makaleye. Şimdiden okuyanların gözlerini ve zihinlerini yoracağım için helallik diliyorum.

    Aslında anlatacaklarım tam bir kiosk uygulaması değil elbette. Ancak bir ucundan dokunuyor. Daha ziyade Unix türevli işletim sistemlerinin popüler KDE, Gnome gibi arayüzlerinde görmeye alıştığımız multi-desktop meselesini anlatmaya çalışacağım. Pek çok kullanıcı bazen muzurluk adına ve bazen de gerçekten işi gereği çoklu masaüstlerine ihtiyaç duyar. İşyerinde çalışırken, başkalarından gizlemek istediği programları başka bir masaüstünde barındırmak isteyen insanların sayısı gerçekten de az değildir. Hatta irili ufaklı pek çok uygulamada vardır sevgili patron ve müdürlerin gözlerinden sizleri korumak için :)
    (more…)

    Paranoyaklar için :)

    // 17 Mayıs 2009 // 5 Yorum » // Delphi, Programlama

    Muhtemelen başlığa bakıp kimse bu yazının ilerleyen satırlarını okumak istemeyecektir, çünkü insan psikolojisi gereği bilinçaltı başlığın kendisine uygun olmadığını düşündürecektir. Ama kimbilir belki yanılırım okuyanlar olur, ne de olsa tüm dünya üzerinde for Dummies serisi makale ve kitaplar nerede ise yok satıyor :)

    Siz değerli okuyucularıma paranoyak demiyorum ama benim bazen paranoyakça davrandığım ve meyvesini bu makale haline getiren bazı olaylar oluyor. Bu makalenin tohumlarının atılmaya başladığı zaman, tam olarak işletim sistemimde bazı gariplikleri hissetiğim zamana tekabül ediyor. Bir müddet önce makinamda bir kısım yavaşlık hissettim, yazdığım yazılar sırasında bu yavaşlığı hissetmem beni hemen keylogger olma ihtimali üzerinde düşündürmeye başlamıştı. Bu bağlamda, Startup, registry’de çalışan uygulamalar listelerini incelemiş, ardından servisleri de gözden geçirmiştim. Gereksiz olduğuna inandığım belki de gerekli olduğu halde sildiğim pek çok program oldu :) Ancak insanın içine şüphe girdimi bir kere imkan yok çıkmıyor :)
    (more…)

    Programımız hafızada kaç KB yer kaplıyor ?

    // 16 Mayıs 2009 // 1 Yorum » // Delphi, Programlama

    Yazdığımız programların hafızada ne kadar yer kapladıkları bilgisini elde etmenin pek çok yolu olmasına rağmen sanırım en iyi yöntem PSAPI.DLL içindeki metodları kullanmak. Bizim sevgili Delphi’miz sağolsun bu DLL’in içindeki metodları psAPI.pas(WinNT process API Interface Unit) içinde deklere etmiş. Bizlere sadece kullanmak kalıyor. Unit’i incelediğimizde pek çok yararlı metodun tanımlı olduğunu gözlemleyebiliyoruz. Processleri listeleyenlerinden tutunda, makinanızdaki device driverları listeleyen yada programınızın gereksiz kullandığı hafıza bloklarını serbest bırakmaya yarayacak bir diğer metod olan EmptyWorkingSet‘e kadar pek çok yararlı metod mevcut.(Bknz.)

    Biz bu metodlardan GetProcessMemoryInfo isimli metodu kullanacağız makalemizde. Bu metod psAPI.pas’da aşağıdaki şekilde tanımlanmıştır:

    function GetProcessMemoryInfo(Process: THandle;
      ppsmemCounters: PPROCESS_MEMORY_COUNTERS; cb: DWORD): BOOL;
    

    (more…)

    Nedir bu Thread’lerden çektiğimiz..!

    // 16 Mayıs 2009 // 8 Yorum » // Delphi, Programlama

    Uzun yıllık programcılık hayatımda pek çok defalar Thread kullandım ancak ilk defa bu kadar yoğun ve derinlemesine kullanma ihtiyacı hissettiğim bir projede çalıştım. Bu projemde TThread sınıfı ile uğraşırken karşılaştığım bazı sorunları sizlerle de paylaşmak istedim. Sizler önlemlerinizi baştan alın ki, saç baş yolmayın…

    Eğer gerçekten time-critical işler yapıyorsanız thread’iniz içerisinde kesinlikle Synchronize kullanmayın.Çünkü Synchronize çağrısı thread’in çalışmasını kesip main thread’e geçirir.

    Eğer siz TThread sınıfından türettiğiniz kendi sınıfınızda Execute metodunda bütün işi Synchronize metoduna teslim etmiş iseniz o zaman siz MTA (Multi Threaded Application) değil STA (Single Threaded Application) geliştiriyorsunuz demektir.. Yani ha formunuzun bir metodunu çağırmışsınız ha bir Thread create etmişsiniz.Arada hiçbir fark olmaz.! Bir örnek vermek gerekir ise:

    TMyThread = class(TThread)
    private
      fErrorString : String;
      procedure CallExternalProc;
    protected
      procedure Execute; override;
    public
      constructor Create;
      property ErrorString read fErrorString write fErrorString;
    end;
    
    constructor TMyThread.Create;
    begin
      inherited Create(true); // Bekler vaziyette oluştur.!
      FreeOnTerminate := true; // Thread terminate olduğunda nesneyi Free et.!
      Resume; // Thread başlasın artık
    end;
    
    procedure TMyThread.CallExternalProc;
    begin
      frmMain.Bilmemne;
      frmMain.BaskabirMetod;
      ...
      ...
     // Burada ise pek çok hesaplama vs. yaptığınızı düşünelim.!
    end;
    
    procedure TMyThread.Execute;
    begin
      inherited;
    
      try
        Synchronize(CallExternalProc);
      except on E: Exception do
      begin
        ErrorString := Format('TMyThread: Error occured. %s, %s', [E.ClassType.ClassName, E.Message]);
      end;
    end;
    

    (more…)

    Uygulamamızın kullandığı hafıza miktarını nasıl düşürebiliriz ?

    // 16 Mayıs 2009 // 8 Yorum » // Delphi, İşletim Sistemi, Programlama, Win32

    Hepimizin bildiği gibi yazdığımız uygulamalar işletim sisteminde Process olarak adlandırılmaktadır. Her bir process en az bir main thread’e sahiptir. Bu iş parçacıkları, derlenmiş ikilik formattaki ham bilginin işletim sisteminin bellek yöneticisi vasıtası ile hafızaya yüklenerek işletilmesi ile hayat bulur. İşletim sistemlerinin bellek yönetimini nasıl yaptıkları son derece karmaşık bir konudur. Bir işletim sisteminde aynı anda birden fazla iş parçacığının çalışabilme ihtimali olduğu için bu işlemlerin bellekte kaplayacakları alanların ve bu bellek bölgeleri arasındaki etkileşimlerin son derece dikkatle hesaplanması gerekmektedir. İşletim sistemlerinde bir process fiziksel bellek ile iletişim halinde değildir. Bunun yerine her bir iş parçacığı için ayrılmış olan hafıza bloklarında her bir işlem kendisini fiziksel belleğin hakimi sanmaktadır. Bu tasarım, her bir işlem parçacığının teoride fiziksel belleğin kendisinden de daha fazla hafıza bloğuna sahip olabilmesini sağlamaktadır. Tüm bunları detaylıca anlatacak ne yerim ne de yeterince malümatım var ancak, bildiklerim ışığında izah etmeye devam edeyim. Process’lerin yani iş parçacıklarının fiziksel bellekten daha fazla alan kullanabilmesine imkan tanıyan teorik yaklaşıma Virtual Memory adı verilir. Virtual Memory modern işletim sistemlerinde disk vb. depolama cihazlarının kapasitelerinin bir kısmının fiziksel hafıza alanı gibi kullanması anlamına gelir. Örneğin Windows işletim sistemlerinde bu pagefile.sys dosyasıdır. İşletim sistemi bir iş parçacığının çalışması için gerekecek hafıza bloğunun karşılanamaması durumunda disk üzerindeki bahsi geçen dosyayı bir hafıza alanı gibi kullanarak fiziksel hafıza alanını rahatlatmaya çalışmaktadır. Ancak RAM’de tutulamayan verilerin sürekli disk ve RAM arasında yazılıp okunması elbette iş parçacığının çalışma performansını olumsuz yönde etkiler.

    İşletim sistemleri, RAM ile sanal bellek arasındaki adreslemeleri fiziksel bellek haritasını kullanarak gerçekleştirirler. Her bir işlem için ayrılmış izole hafıza bloğundaki $101112 gibi bir adres fiziksel adresteki aynı adres demek değildir. Bu adresin fiziksel adresteki karşılığı başka bir tabloda tutulur ve fiziksel hafızaya erişim için bu tablodan istifade edilir. Modern işletim sistemleri, iş parçacıkları için ayırdıkları hafıza bloklarının tamamen izole olmasından sorumludurlar. Bir iş parçacığının diğer birisinin kullandığı hafıza bloğuna erişimi, o blokta yapabileceği değişiklikler tehlikeli durumlara sebebiyet verecektir. İşte bu sebeple arada sırada da olsa Protection Fault gibi hatalar alırız. Aynı zamanda işletim sistemleri, mevcut fiziksel hafızayı iş parçacıkları üzerinde adil dağıtma gibi bir misyonu da bünyelerinde barındırmak durumundadırlar.
    (more…)

    Tuşa basma işkencesi..

    // 15 Mayıs 2009 // Yorum Yok » // Delphi, Programlama

    Diyelim ki bir program yazdınız ve programınızın içine sürpriz yumurta koymak istiyorsunuz.Yada kullanıcılara feci eziyet etmek istiyorsunuz. Bu amaçla çok zorlu bir tuş kombinasyonu kullanmak istiyorsunuz. Mesela biz bu örneğimizde Ctrl + Shift + F5 + 7 + 8 tuşlarına basılınca birşeyler yapacağız. (Tabii bu tuşlara aynı anda basmayı becerebilirsek :) )

    Yine herzaman ki gibi ne yapıyoruz. Tabii ki önce Help’e bir uğruyoruz. Ve Help’imize GetKeyState yazıyoruz. O da ne TDragObject diye abuk bir class’a ait bir yardım çıktı.Yok yok biz bunu aramıyoruz. Windows API helpinden bakacağız.Heh şimdi bulduk istediğimiz fonksiyonu.Fonksiyondan görebildiğimiz kadarı ile dönüş değeri SHORT diye bir tip. Bu SHORT’da ne ola, diyorsanız o zaman hemen yolumuzu windows.pas’a çeviriyoruz.Ve GetKeyState aratıyoruz. Ve 20.853 ‘üncü satırda şöyle bir tanımlama ile karşılaşıyoruz.

      function GetKeyState(nVirtKey: Integer): SHORT; stdcall;
    

    Eee burada da SHORT diyor. Biz merak ediyoruz, programcı meraklı olur ;) SHORT neye tekabül ediyor diye ve en sonunda buluyoruz.
    Yine Windows.pas satır 155 de şöyle bir tanım var.
    (more…)