You are on page 1of 26

Delphi ile Thread(Kanal) Kullanm Blm 1

Posted on 02 Ekim 2007 | 22 Yorum Yaptmz projelerin bir blm eninde sonunda bu konuya dayanyor. Genelde kanal gerekli olduu halde Timer ya da Application.ProcessMessages gibi arelere gitmeye alyoruz Ve devamnda hem grsel hem de ileyi asndan istemediimiz sonularla karlayoruz. Halbuki kanallarn kullanlmas sanld kadar zor deildir. Bu n yargy bu makalede amaya alacaz. Bu makale, ileri seviye Windows programclna gei kapsdr. Bu makeledeki konular rendikten sonra Win32 programclnn en gl zelliklerini renmi olacaksnz. Yanlz "ileri seviye" tabiri gznz korkutmasn. nk makeleyi bitirdiinizde thread kullanmnn aslnda ne kadar kolay olduunu greceksiniz. Bununla birlikte bu makalede bu konu ile ilgili her ayrnty anlatabilmemiz iin yzlerce sayfalk kitap yazmamz gereklidir. Bu yzden burada iin mantn kapp, ihtiyacnz olduka yardm dosyalarna mracaat etmeniz gerekebilir. Kanallar hakknda hi bir bilginiz olmayabilir. Bu makalede iin temelinden alacaz ve makaleyi bitirdiinizde kendi programlarnza kanal ekleyebilecek duruma geleceinize inanyorum. Bu makale serisinde kanallar temelden anlatlmakla beraber, ileri seviye kanal kullanm, VCL bileenlerinde kanal kullanm ve Muteksler de ana konularmz arasna girecektir.

Giri
lk bata bir ka teorik bilgi ile balamak istiyorum. Bu bilgilieri olabildiince skmadan, ksa tutmaya alacam. Disk zerindeki her bir dosya altrldnda artk windows iin birer ilem(process) olurlar. Bir ilem Windows iin fazla bir ey ifade etmez. nk ilemler sadece hafzada belli bir blgede var olmaktan sorumludur. Esas ilemi yapan ksm kanallardr(thread). Bir ilem en az bir adet kanala sahiptir. Win 3.1 gibi iletim sistemleri sadece bir adet kanala sahiptir. Ama Windows 95 ve st, Unix, OSX gibi iletim sistemleri birden fazla kanala sahip olabilirler. letim sistemi bir program ya da bir DLLi ilk bata ilem olarak hafzaya tar. Bu esnada ilem eylemsiz olarak durur. Bu ileme ait kanallar ise bizim belirlediimiz lde programn kodlarn altrmaya balar. Yine teorik olarak bilmemiz gereken dier bir terim de quantum deeridir. Her kanaln windows tarafndan atanm bir iletilme sresi vardr. Ve terim olarak quantum olarak isimlendirilir. Bunun sadece tanmn bilmeniz yeterli.

Her kanal, alma esnasnda kendi eax, ebx, edx gibi register verilerini tutan bir yapya sahiptir. Bu yapya context(ierik) ad verilmektedir. Bu yapnn Delphideki karl TContext olup, Windows unitindedir. Bu record tipini incelediinizde bir ok register barndrdn greceksiniz. Eer buradaki Delphi ve Assembler ile ilgili makaleyi ve buradaki fonksiyon arm makenizmalarn okudu iseniz imdi anlatacaklarmz size tandk gelecektir. nk kanallarn ileyi mant fonksiyon armlarna benzemektedir. Ama devam edebilmek iin bunlar okumak zorunda deilsiniz. Her bir kanal sahip olduu ncelie gre iletim sistemi tarafndan ileme alnacaktr. Bu ileme al esnasnda sahip olduu contextdeki register deerleri yklenir. Esp, Ebp, Eip gibi registerlar da yklendiinden, kanal nceden kald yerden nceki stack deerlerine gre ilemine devam edecektir. Ve yine nceliine gre belli bir sre sonunda iletim sistemi bu kanal durdurup register deerlerini context yapsnda saklar. Daha sonra dier bir kanaln context verisini ykleyerek baka bir kanal ilemeye geer. Ve hakeza bu ilem tm kanallar iin devam edip gider. letilme ve gei sreleri ok ksa olduundan, biz sanki ayn anda tm kanallar iletiliyormu gibi zannederiz. Halbuki btn kanallar belli bir sra ile ok ksa srelerde iletilip dier bir kanala geilmektedir. Yani her kanal ayn anda almaz, biz yle zannederiz. Tabi bu anlattklarmz tek ilemci ve tek ekirdek iin geerlidir. Kanallarn iletilme srelerini ise kanallarn ncelii belirlemektedir. Hangi kanal daha yksek ncelikli ise o kanal daha fazla iletilme sresine sahip olur. Eer kanal arkaplanda alma zere ayarlanm ise, dier kanallar ilemlerini tamamladktan sonra ancak bu kanala sra gelecektir. Eer kanal ok yksek ncelie sahip ise uzun bir sre bu kanal ileme devam edecek ve dier kanallara sra gelmeyecektir. Demek ki, ayn anda bir ka ilem yapmak istiyorsak kafa patlatp, parmak yormamza gerek yokmu. nk iletim sistemi zaten sizin iin bu sistemi kurmutur. Sadece biraz vaktiniz ayrmanz ve kanallar nasl oluturuluyor renmeniz yeterlidir.

Kanal Oluturma Yntemleri


Aslnda byle bir balkla konuya giri yapmak istemezdim. Ama kanal oluturmay renmeden nce bir ka eyi aa kavuturmak istiyorum. Gerek forumlardan gerekse haber guruplarndan kanallarla ilgili gelen sorulardan bir ou kanallarn yanl oluturulmas zerine kaynaklanan yanllklardan gelmektedir. Normalde bir kanal windows ortamnda CreateThread Windows APIsi ile oluturulur. Fakat VCL bize TThread isminde bir snf da sunmutur. Kullanm asndan ayrt edebileceiniz temel nokta udur. Eer kanalda yapacanz ilemler VCL snflarn etkilemesi gerekiyorsa TThread snfn kanal oluturmak iin kullanyoruz. Yok eer VCL snflarn ilgilendiren bir ilem sz konusu deilse CreateThread APIsini kullanyoruz. Bunun neden byle olduunu ileride VCL ve Kanal kullanm ile alakal balkda vereceim. Muhtemelen Blm 2de greceksiniz. Bu yzden biraz sabr. Balarken bu ekilde bir giri yapmamn sebibi buradan kaynaklanyor. Bir ok kii belki de Windows APIlerine kar bir soukluktan dolay CreateThread ile uramak istemiyor ve CreateThread ile ilgili ksmlar gz kapal atlayabiliyor. Bu yzden eksik kanal bilgisi ile ileride bir ok sorunla karlaabiliyor. Ama imdi greceimiz gibi kanal oluturma ilemi sanld kadar zor ve karmak bir ilem deil.

Bir Kanal Oluturalm


Kanal oluturma ile sorumlu Windows fonksiyonu CreateThread fonksiyonudur ve aadaki gibi tanmlanmtr:
function CreateThread(lpThreadAttributes: Pointer; dwStackSize: DWORD; lpStartAddress: TFNThreadStartRoutine; lpParameter: Pointer; dwCreationFlags: DWORD; var lpThreadId: DWORD): THandle; stdcall;

Bu fonksiyonun parametrelerinin ksaca aklamasn verelim. lpThreadAttributes: Kanalmza atanacak olan bir ok gvenlik zelliini belirtmemize yarar. Eer nil girerseniz varsaylan gvenlik zellikleri kullanlacaktr. Win9x ve WinMe iin bu deer nil olmaldr. Bu konu ile alakal daha fazla bilgi almak iin sdk yardm dosyalarndan SECURITY_ATTRIBUTES balna mracaat edebilirsiniz. dwStackSize: Eer kanal iin zel bir stack bykl belirlemeyecekseniz bu deeri 0 olarak girmelisiniz. Bylelikle kanaln stack bykl program ile ayn olacaktr. Gerektiinde stack boyutu otomatik olarak geniletilebilir. lpStartAddress: Bu parametre esas ii gren parametredir. nk kanal almaya bu parametredeki fonksiyon iaretisi ile beraber balar. Burada tek yapmamz gereken fonksiyonun isminin nne @ iareti vererek girmektir. lpParameter: Bir nceki parametrede girdiimiz kanal fonksiyonuna parametre yollamak istiyorsanz, bu parametrelerin iinde bulunduu bir recordun adresini buraya girmelisiniz. Ayrca record haricinde karakter veya say deelerini de parametre olarak yollayabiliriz. dwCreationFlags: Bu parametre ile kanaln oluturulur oluturulmaz alp almayacan belirliyoruz. Eer bu parametreye CREATE_SUSPENDED girerseniz, kanal oluturulur fakat kendisine almaya balamas iin izin verilmez. Bu durumda siz ResumeThread fonksiyonu ile bu kanala alma talimat vermedike kanal o ekilde almadan duracaktr. Bir kez ResumeThread ile almaya baladktan sonra SuspendThread ile tekrar kanal durdurabilirsiniz. Eer bu parametreye 0 girerseniz, kanal oluturulur oluturulmaz almak iin sraya girecektir. lpThreadId: Bu parametreye kanal kimlik IDsi atanr. Buraya deeri olmayan bir Dword deiken giriyoruz. Deiken gireceiz nk "var" olarak tanmlanm. nk kanal oluturulduktan sonra Windows NT,2000 ve st iletim sistemlerinde kanaln IDsi bu deikene yklenir. Windows 9x/Me iletim sistemlerinde bu deer daima 0dr. imdi gelin bu fonksiyonu kullanarak bir kanal oluturalm.
var KanalID: DWORD; begin CreateThread(nil, 0, @KanalFonksiyonu, nil, 0, KanalID);

te hepsi bu! Tek yaptmz sadece gerekli parametreleri kullanmak, gerisini duruma gre nil veya 0 yapmak. Bu fonskiyon arldnda KanalFonksiyonu isimli fonksiyonumuz kanal iinde almaya balayacaktr. Dilerseniz KanalFonksiyonu isimli fonksiyonumuzun tanmlamasn imdi yapacamz rnek iinde verelim.

Basit Bir rnek


imdi oluturduumuz bu kanaln nasl altn basit bir rnek ile grmeye alalm. Yeni bir uygulama oluturalm ve Form zerine bir adet button ve bir adet label koyalm. Buttonnun Caption zelliini "Olutur ve altr" olarak deitirelim. Ve Buttonnun OnClick olayna kanalmz oluturan u kodlar girelim:
procedure TForm1.Button1Click(Sender: TObject); var KimlikID: DWORD; begin CreateThread(nil, 0, @KanalFonksiyonu, nil, 0, KimlikID); end;

imdi de kanalmzda esas ii yapan KanalFonksiyonu isimli fonksiyonumuzun tanmlamasn girelim:


function KanalFonksiyonu(P: Pointer): Longint; stdcall; ... implementation ... function KanalFonksiyonu(P: Pointer): Longint; stdcall; var i: Integer; Toplam: Int64; begin Toplam := 0; for i := 0 to 1000000000 do begin Toplam := Toplam + i; Toplam := Toplam shl 4; end; Form1.Label1.Caption := 'Kanal ilemini tamamlad. Sonu:' + IntToStr(Toplam); end;

Kanal fonksiyonumuzun tanmlamasn istediimiz gibi yapabilirdik. Burada mesela P: Pointer parametresine ihtiya yoktu. Ayrca Longint ktsn vermeye de ihtiyacmz yok. CreateThread fonksiyonunun parametre aklamalarn hatrlarsanz, kanal fonksiyonumuza belli parametreler yollayabiliyorduk. te bu parametreleri pointer olarak buradan alyoruz. Ama bu meseleye sonra geeceiz. Ayrca fonksiyon tanmlamasndaki stdcall ifadesine yabanc iseniz buradaki fonksiyon arm makenizmalar ile ilgili makaleyi okuyabilirsiniz.

Bu fonksiyonda ekstra bir ey yapmyoruz. Sadece bir for dngs var ve matematiksel bir ilem yaplyor. For dngs bittiinde ise formdaki labela bir sonu yazdryoruz. Bu for dngsn, ayr bir kanalda deil de programn ana kanalnda altrmaya kalksaydk muhtemelen Application.ProcessMessages ve TTimer gibi zmlere gidecektik. Bu durum da ok fazla miktarda performans dklne sebep olacakt. Ayrca ilemlerimiz genelde buradaki gibi basit matematiksel ilemler olmadndan, programda kmelere ve donmalara sebep olacaktk. Bu program altralm ama buttona basmayalm. Eer ak deilse View mensnden "Thread Status" penceresini ve "Event Log" penceresini aalm ve grebileceimiz bir keye yerletirelim. Eer BDS 2005 ve st kullanyorsanz bu pencereler debug esnasnda altta grnr biimde hazr olacaklardr. Ardndan buttona bir kez basalm ve "Thread Status" penceresine yeni bir eleman eklendiine dikkat edelim. Eer buradaki eleman ok abuk bir ekilde grnp kayboldu ise ilemciniz benimkinden hzl demektir . Bu yzden for dngsndeki limit deeri biraz artrp tekrar altralm. Buttona her tkladnzda "Thread Status" pencersinden de takip edebileceiniz gibi yeni bir kanal eklenecek. Tabi butona basma iini o kadar ok abartmayn. Her kanal oluturulduunda kendisine bir ID atanacak. rneimizde bu deer KimlikID deikeninde tutuluyor. "Thread Status" pencersinde ise ThreadID stununda bu deeri grebiliriz. Her kanal ilemini bitirdiinde "Thread Status" pencersinden kaybolduunu greceksiniz. Ayn ekilde "Event Log" pencersinde de kanaln IDsine gre balama ve bitii ile ilgili mesajlar grebilirsiniz. Kanal alrken program zerinde istediiniz ileme devam edebilirsiniz. Formu tayabilir, boyutlandrabilir, baka bileen ve nesneler var ise onlar kullanabilirsiniz. Ve bunu yaparken bir yandan kanal(lar) da almaktadr. Ayn kanal fonksiyonunu dorudan armaya kalktmzda formumuza dng bitene kadar mdahale edemeyecektik. Grdnz gibi bir ka satr kod ile kanal oluturuveriyoruz. Tek bilmemiz gereken ksm CreateThread fonksiyonunun parametreleri. Bunlarn ounu da kullanmadk. Ama makalenin ilerleyen ksmlarnda bunlar da yava yava kullanma dahil edeceiz. te bir tanesi geliyor.

Kanal Fonksiyonlarna Parametre Gnderimi


CreateThread fonksiyonunun parametrelerini aklarken lpParameterden sz etmitik ve bu parametre ile kanal fonksiyonuna istediimiz parametreyi gnderebileceimizi belirtmitik. imdi bunu nasl yapacamz grelim. Bunun iin yukarda yaptmz rnekten faydalanacaz. lk bata type blounda aadaki record tanmlamasn yapalm.
PKanalParametresi = ^TKanalParametresi;

TKanalParametresi = record BirParametre: string; end;

Ardndan kanal fonksiyonumuzu aadaki gibi deitirelim.


function KanalFonksiyonu(P: Pointer): Longint; stdcall; var Parametreler: PKanalParametresi; begin Parametreler := PKanalParametresi(P); //Parametremizi alalm. while True do begin Form1.Label1.Caption := Form1.Label1.Caption + Parametreler^.BirParametre; Sleep(1000); //lemcinin %99 almasn istemiyiz deil mi.... end; Dispose(Parametreler); end;

En son olarak buttonnun OnClick olayn da aadaki gibi olacak ekilde deitirelim.
procedure TForm1.Button1Click(Sender: TObject); var KimlikID: DWORD; Parametreler: PKanalParametresi; begin New(Parametreler); Parametreler^.BirParametre := 'a'; CreateThread(nil, 0, @KanalFonksiyonu, Parametreler, 0, KimlikID); end;

Aslnda ok fazla bir ey yapmadk(en azndan siz kopyala yaptr yaptnz:) ). Burada basit bir iareti kullanmn gryoruz. Ve parametre olarak bir recordu kullandk. Record yerine baka veri tiplerini de kullanabilirdik. Ama genelde karlalan sorunlar recordlar parametre olarak yollamaktan gelmektedir. En bata verdiimiz record tanmlamasna bakarsanz, PKanalParametresi isminde bu recordumuza iareti olan bir tip greceksiniz. Parametremizi iareti olarak kullanacamzdan bu tipe ihtiyacmz olacak. Ardndan Buttonnun OnClick olayna bakalm. Burada nceki rnee gre fazladan 2 satr ekledik. lk bata var blounda Parametreler isminde bir record iaretisi tanmladk. Ardndan iareti hi bir ey ifade etmediinden, iaretiye ait bir recordun hafzada oluturulmas iin New rutinini kullandk. Devamnda bu recordun deikenlerinden birine bir string deer atadk. CreateThread fonksiyonu parametreyi iareti olarak istediinden buraya direk olarak Parametreler deikenimizi girebiliriz. nk bu deiken bir record iaretsidir. En son olarak yeni kanal fonksiyonumuza baktmzda, iareti olarak gelen parametremizin nasl kullanldn gryoruz. Burada bizim iin nemli olan ksm Dispose ksmdr. nk burada New ile oluturduumuz ve iaretiye ait olan recordu hafzadan siliyoruz.

Record iaretisi yerine normal record kullansa idik ve sonra da bunu parametre olarak geirirken pointer olarak geirse idik olmazmyd. Olurdu stelik New ve Dispose rutinlerini de kullanmak zorunda kalmazdk. Ama, bu durumda bo yere hafzda yer igal edecektik. nk parametre recordu sadece bir kerelik iimize yarayacak. Bu yzden iimiz bitince Dispose ile hafzdan siliyoruz. Bu rneimizi de altrp deneyebilirsiniz. Ayrca Grev Yneticisi ile ilemciyi yzde ka kullandn da grebilirsiniz. Tabi burada Sleep rutinini kullanmak yerine daha baka yaplabilecek eyler var ama imdilik kafa kartrmamak iin bunlar sonraya brakyorum.

Sonu
Bu blm burada noktalayalm. Gelecek blmde kanallarn nasl birbirleri ile birlikte alabileceini greceiz. Ayrca Mutexler ve VCL ile kanal kullanm da gelecek blmde greceimiz konular arasnda olacak. Bylece kanallar konusunda hi bir pheniz, korkunuz kalmayaca gibi programlarnza da profesyonellik katacaksnz. Gelecek blmde bulumak midi ile Yorumlarnz ve eletirilerinizi bekliyorum. Yine her zaman ki gibi sorularnz buradan yada Delphi Trkiye forumlarndan iletebilirsiniz Fatih Tolga Ata 2007

Kaynaklar

Windows SDK Yardm Dosyas Delphi 4 Unleashed, Charlie Calvert, Sams Pub., 1999 AsyncCalls i winapi ve TThread sinifi ile ugrasmak istemeyen arkadaslara tavsiye ederim. yazdiginiz her hangi bir fonksiyonu parametre olarak belirtip ilgili fonksiyonun ayri bir thread icinde calismasini sagliyor. http://andy.jgknet.de/async/ bu ipucunun ardindan yaziyla ilgili olarak sunu sormak istiyorum kanal olusturma yontemlerini ogrendik peki ya kanal durdurma yontemlerini ne zaman ogrenecez ? terminate ile cok guzel sonlaniyor bu kanallar ama benim ugrasipta yapamadigiim calisan bir kanalin calismasini ana kanaldan aninda durdurmak. mesela icinde kanalin durdurulmasi gerektigini kontrol eden herhangi bir kod barindirmayan bir kanal nasil durdurulur. kanal da su kodun calistigini varsayalim.
while true do noop

ben ana kanaldan bu kanali durdurmayi basaramadim. ikinci bir husus ise araya vcl i karistirinca kanallar bazen kilitlenmeye neden oluyor. mesela AsyncCalls kullanilarak hazirlanmis surdaki ornekte birinci start dugmesine basinca progressbar adim adim ilerlemeye basliyor ve o sirada biz formdaki diger elemanlari gayet rahat bir sekilde kullanmaya devam edebiliyoruz. ne zaman ki ikinci start dugmesine basiyoruz progressbarlar ilerlemesine ragmen form ve uzerindeki bilesenler donuyorlar http://rapidshare.com/files/60459720/ornek1.rar.html makalenin ikinci baskisi gelene kadar database ve thread icin, huseyin hocam suraya goz atabilirsin. http://delphi.about.com/od/kbthread/a/query_threading.htm

Delphi ile Thread(Kanal) Kullanm Blm 2


Posted on 06 Ekim 2007 | 19 Yorum Bu blmde kanallar nasl e zamanl olarak altrabileceinizi greceksiniz. Bunun iin kritik blgeler ve muteksler ile tanacaksnz. Ayrca kanal uyumlu olmayan VCLin, kanallar ile nasl kullanlabileceini de greceksiniz. Bununla beraber kanal kullanmndaki yaanan baz problemlere de deinmeye alacaz. zellikle veritabanlarn kanallar ile kullanmada uyulmas gereken pf noktalarna da deinmeye alacaz. Aslnda bu blm gerekten uzun oldu. Normalde VCL kullanmn nc bir blme tamay dnyordum. Ama bu konuda istekler olunca birletrip yaynlamay ddm. eride sizi iki sayfalk bir makale bekliyor. Bu yzden sayfa sonunda bitti zannedip kapatmayn! Hazrsanz balayalm.

Kanallar E Zamanl Olarak altrmak


Kanallar ile urarken karlalan en byk sorunlardan bir tanesi de phesiz kanallar ortak bir ekilde sorunsuz olarak altrmaktr. Eminim kanallar ile uraan birisi btn kanallar tarafndan ortak olarak kullanlan bir kaynaa erimede problemlerle kar karya gelmitir ve kanallar bir keye itmesine sebep olmutur. Mesela u veritaban problemi size tandk gelecektir. Diyelim ki, iki adet kanalmz var ve bunlardan birincisi veritabanndaki bir kayd ap deiiklik yapmaya balad. Sonra ikinci kanal ayn kaydn belli blmlerinde dzenlemeler yaptktan sonra veriyi kaydetti. Ardndan birinci kanal, ayn verilerdeki deiikliini bitirdi ve kaydetti. Birinci kanal, ikinci kanaln deiikliklerinden habersiz olduundan ikinci kanaln yapt dzenlemeler de utu gitti. Burada okuduunuz problem, normalde kanallar ve veritaban ile uraan bir ok programcnn bana gelmi bir problemdir. Bununla birlikte sadece veritabannda deil bir ok noktada zellikle bir dosyay, bir portu, bir DLLi veya baka bir eyi, kanallarn ortak kullanmasnda hep benzer skntlarla karlalmtr. Bu gibi skntlarn ve problemlerin zm kanallar e zamanl kullanm iin kritik blgeler tanmlamak ya da muteksleri kullanmaktr. Bu iki yntem de makalemizin konular arasndadr.

Kritik Blgeler(Critical Sections)


Kritik Blgeler -ya da baka yerlerde grebileceiniz gibi kritik kesimler, blmler, vs..- yukarda anlatmz problemdeki, birinci kanaln kayna kullanmas esnasnda ikinci kanaln bu kaynaa erimesini englelemek iin kullanlr. Birinci kanal iini yapyorken, ikinci kanala almas iin bir zaman dili ayrlmaz. Bylece birinci kanal iini bitirene kadar ikinci kanal altrlmaz. Bunun iin rnek bir uygulamada yukardaki anlattmz senaryolara benzer bir problem oluturalm ve zmn aramaya alalm. Diyelim ki, iki adet kanalmz var ve bu kanallar bir dngnn her admnda bir dizi ilem gerekletiriyor. Ve bu gerekletirdikleri ilemler iin ortak kullandklar global olarak tanmlanm bir deiken olsun. Meseleyi anlatabilmek iin iki kanalnda

yapt ilemlerin sonucunun ayn olmasn salamak istiyorum. Bylece problem daha iyi anlalabilecek. Birinci kanal bir dngnn her admnda global deikenimize teker teker harfler ekliyor ve sonuta deikenimizde bir yaz oluuyor. Ayn ilemi ikinci kanalmz da yapyor ve sonu olarak birinci kanal ile ayn yazy retiyorlar. Yanlz ikinci kanal harfleri teker teker eklemek yerine ikier ikier ekliyor. Ekleme ilemi ok hzl olduundan problemin olumas iin aralara Sleep rutinini ekleyerek ilemi birazck yavalatyoruz. imdi bu mantmz koda dkmeye balayalm. Yeni bir VCL uygulamas aalm ve bir adet button ekleyelim. Aadaki gibi unitin var blounda global bir deiken tanmlayalm:
var Form1: TForm1; SonucYazi: string; //Global deikenimiz

Ardndan buttonun OnClick olayn aadaki gibi deitirelim.


procedure TForm1.Button1Click(Sender: TObject); var Kanal1ID, Kanal2ID: DWORD; begin CreateThread(nil, 0, @Kanal1, nil, 0, Kanal1ID); CreateThread(nil, 0, @Kanal2, nil, 0, Kanal2ID); end;

Bu iki adet kanalmza ait kanal fonksiyonlarn da aadaki gibi oluturalm:


procedure Kanal1; procedure Kanal2; ... implementation ... procedure Kanal1; var i: Integer; begin for i := 0 to 10 do begin SonucYazi := SonucYazi Sleep(10); SonucYazi := SonucYazi SonucYazi := SonucYazi SonucYazi := SonucYazi Sleep(10); SonucYazi := SonucYazi SonucYazi := SonucYazi SonucYazi := SonucYazi Sleep(10); end; end;

+ 'd'; + 'i'; + 'y'; + 'e'; + 'z'; + 'o'; + 'n ';

procedure Kanal2; var i: Integer; begin for i := 0 to 10 do begin SonucYazi := SonucYazi Sleep(10); SonucYazi := SonucYazi SonucYazi := SonucYazi Sleep(10); SonucYazi := SonucYazi end; end;

+ 'di'; + 'ye'; + 'zo'; + 'n ';

Grdnz gibi iki kanal fonksiyonu da ayn sonucu retiyor. Ama sonucu retme yollar farkl. Ayrca iki kanal da pe pee oluturulup altrlyor. Forma bir adet daha button ekleyelim ve bize sonucu gstersin. Bunun iin ikinci butonumuzun OnClick olayn aadaki gibi dzenleyelim:
procedure TForm1.Button2Click(Sender: TObject); begin ShowMessage(SonucYazi); end;

Bu program altrdktan sonra ve kanallar birinci dme ile altrdktan sonra sonu yaznn "diyezon diyezon diyezon " gibi bir ey olmasn isteriz. Yani bir for dngsnn bir adm tamamlanmadan SonucYazi isimli global deikene mdahale olmasn istemeyiz. Halbuki program altrdnzda sonucun hi de byle olmadn greceiz. Kanal1 iini bitirip "diyezon " yazmadan Kanal2 ie karm ve aralarda kendi harflerini eklemitir. Hakeza bu, Kanal2 iin de geerlidir. Yani Kanal2, dngnn bir admn bitirip istenilen sonucu veremeden Kanal1 global deikenimize mdahale etmitir. Burada verdiimiz rnek biraz soyut kaabilir. nk yaptmz ilemden sonu olarak elimize bir ey gemiyor. Ama karlaacanz sorunlar hep bu tarzda sorunlardr. Karlatnz sorunlar verdiimiz bu rnek ile kyaslama yapabilirsiniz. Mesela biz bu rneimizde ilem admlarn yavalatmak iin Sleep rutinini kullandk. Normal hayatta kanallar ile urarken zaten yaptmz ilemlerin admlar Sleep rutinini aratmamaktadr. Mesela bir veritaban tablosunda Edit ve Post gibi metodlar kullandmzda belli bir miktar ilemin tamamlanmasn bekleriz. Bir de eer sizde sonu dzgn kyorsa yine sylyorum ilemciniz, bu makaleyi yazdm sralardaki ilemcimden hzl demektir. Bu durumda Sleep rutinine verdiimiz deerleri ok az miktarlarda artrmay deneyin. Sadede gelelim ve problemi anlad isek yle bir soruyu kafamza getirelim: Bir kanal belli bir iini bitirmeden nce dier kanallarn mdahalesini nasl engelleyebiliriz? Cevab zaten bu blmn banda verdik. Bunun iin belli yerleri kritik blge ilan edeceiz. Bir kanalda kritik blge ilan ettiimiz yerdeki kodlar almaya balad srada, dier kanallardaki ayn kritik blgedeki kodlar iletilmez ve bekler. Yani ayn kritik blgeye sahip kanallardan ayn anda sadece bir blgedeki kodlar altrlabilir. Bylece dier kanallarn istenilmeyen mdahalesi engellenmi olur. Bir Kritik blge tanmlamak iin bilmemiz gereken drt adet Windows apisi vardr.

InitializeCriticalSection: Bu fonksiyon kritik blgeyi temsil eden recordun ilk deerlerini atamakla sorumludur. Yani kritik blgeyi kullanma hazrlamaktadr. Genelde formun OnCreate olaynda kullanlr. DeleteCriticalSection: Yukardaki fonksiyonun tam tersi olarak, bu fonksiyon da kritik blgeyi ortadan kaldrr. Genelde formun OnDestroy olayna yerletirilir. EnterCriticalSection: Bu fonksiyonu altrdnz satrdan itibaren kritik blgenin balangcn belirtirsiniz. Yani Delphideki "begin" kelimesi gibi dnebilirsiniz. LeaveCriticalSection: Bu fonksiyon ise, altrld satrla kritik blgeyi snrlandrr. Yine Delphideki "end" ifadesine benzetebiliriz. Kritik blgeyi temsil eden record ise TRTLCriticalSection tipindedir. Normalde Windows SDK yardm dosyalarnda LPCRITICAL_SECTION eklinde tanmlanmtr. Ama Delphi ekibi bir ok windows yapsn tanmlarken bunlara "T" ile balayan daha iyi okunur alias isimler vermiler. Her neyse Kritik blgenin tanmlanmas gayet basittir. Bir kritik blgeyi iletim sistemine tantmak iin InitializeCriticalSection fonksiyonunu kullanyoruz. Kritik blge ile iimiz bittiinde de DeleteCriticalSection fonksiyonunu armay unutmuyoruz. Ardndan kanal fonksiyonlarmzda kritik blgeleri belirleyip bana EnterCriticalSection ve sonuna LeaveCriticalSection sonksiyonlarn koyuyoruz. rneimize dnelim ve kritik blgeleri eklemeye balayalm. Bizim mdahale istemediimiz blge, kanal fonksiyonlarndaki dnglerin her bir admdr. Yani teker teker veya ikier ikier ekleme yaplan ksmlarda bir adm iini bitirmeden baka bir kanaln global deikenimize mdahalesini engellemek istiyoruz. Ksacas kritik blgemiz bu rnekte, dngnn her bir adm olmaldr. lk bata global olarak bir deiken daha tanmlayalm. Bu deiken bizim kritik blgemizi temsil eden record olsun.
var Form1: TForm1; SonucYazi: string; BirinciKritikBolge: TRTLCriticalSection; //kritik blgemizi temsil ediyor.

Ardndan formun OnCreate ve OnDestroy olaylarn aadaki gibi deitirelim.


procedure TForm1.FormCreate(Sender: TObject); begin InitializeCriticalSection(BirinciKritikBolge); end; procedure TForm1.FormDestroy(Sender: TObject); begin DeleteCriticalSection(BirinciKritikBolge); end;

Eer baka kritik blgeler de tanmlam iseniz bunlar da InitializeCriticalSection ve DeleteCriticalSection fonksiyonlarn kullanarak oluturmal ve silme ilemlerini gerekletirmelisiniz. Bizim rneimizde bir adet kritik blge yeterlidir. imidi kanal fonksiyonlarnda bahsettiimiz yerleri kritik blge olarak ilan edelim.
procedure Kanal1; var i: Integer;

begin for i := 0 to 10 do begin EnterCriticalSection(BirinciKritikBolge); //BirinciKritikBolge balangc SonucYazi := SonucYazi + 'd'; Sleep(10); SonucYazi := SonucYazi + 'i'; SonucYazi := SonucYazi + 'y'; SonucYazi := SonucYazi + 'e'; Sleep(10); SonucYazi := SonucYazi + 'z'; SonucYazi := SonucYazi + 'o'; SonucYazi := SonucYazi + 'n '; Sleep(10); LeaveCriticalSection(BirinciKritikBolge); //BirinciKritikBolge bitii end; end; procedure Kanal2; var i: Integer; begin for i := 0 to 10 do begin EnterCriticalSection(BirinciKritikBolge); //BirinciKritikBolge balangc SonucYazi := SonucYazi + 'di'; Sleep(10); SonucYazi := SonucYazi + 'ye'; SonucYazi := SonucYazi + 'zo'; Sleep(10); SonucYazi := SonucYazi + 'n '; LeaveCriticalSection(BirinciKritikBolge); //BirinciKritikBolge bitii end; end;

Yapacamz baka bir ilem kalmad. Program altrp deneybiliriz. Sonucun "diyezon diyezon diyezon " gibi dzgn olduunu greceiz. nk Kritik blge tanmlayarak sadece bir kanaln kritik blgesini altrm olduk. Bylece bir kanal global SonucYazi deikeni ile urarken dier kanal buna mdahale edemeyecektir. Burada bir adet kritik blge tanmladk. Sizler ihtiyaca gre projelerinizde bir ok kritik blge tanmlayabilirsiniz. Ayrca kritik blgelerin birbirinden bamsz hareket ettiini de aklnzdan karmayn. Bazen kartrlan nokta bu ksm olabiliyor. Mesela bu rneimizde bir adet kritik blge mevcut ama iki farkl yaplan i var. Grdnz gibi kanallar e zamanl olarak altrmak o kadar da zor deilmi. Tek yaptmz bell blgeleri kritik blge olarak ilan etmek. imdi yeni bir kavramla daha tanalm.

Muteksler(Mutex)

ngilizce de, mutual ve exclusive kelimelerinin birleiminden meydana gelmitir. Dilimizde ise mterek ve sekin kelimeleri bu kelimeleri karlamaktadr. Programlama terminolojisinde ise, sadece tek ilemdeki deil birden fazla ilemdeki kanallar e zamanl olarak kullanlmasn salayan bir tekniktir. Muteksler, alma mant olarak kritik blgelere benzerler fakat fazladan bir zellik olarak sadece ayn ilem yani sadece ayn uygulamada deil baka uygulamadaki kanallar ile e zamanl olarak almay da salarlar. Bir mutekse ayn anda sadece bir tek kanal sahip olabilir. Literatrde, bu mutekse, bir kanal sahip olduunda, muteksin kanal tarafndan "tetiklendii" ya da "tutulduu" sylenir. Ayn ekilde eer kanal bu mutekse sahip deiilse muteks "serbestir" ya da "tetiklenmemitir" tabirleri kullanlr. u an kafanzda mutekslerin ne olduuna dair somut bir dnce olumam olabilir. Ama blmn sonuna kadar okuduunuzda neyin ne olduuna dair bir fikir oluacana eminim. Bir muteks, CreateMutex fonksiyonu ile oluturulur. Bu fonksiyonun tanmlanmas ve aklamas aadadr.
function CreateMutex(lpMutexAttributes: PSecurityAttributes; bInitialOwner: BOOL; lpName: PChar): THandle;

lpMutexAttributes: Muteksin gvenlii ile ilgli parametredir. Bu parametreye nil girerek varsaylan gvenlik zelliklerini ayarlam oluyoruz. bInitialOwner: Kanaln bu mutekse sahip olup olmadn ya da yukarda bahsettiimiz gibi muteksin tetiklenip tetiklenmeyeceini belirler. True ya da False giriyoruz. Genelde muteksi programn ana kanalnda oluturduumuzdan, ana kanaln mutekse sahip olmasn istemeyiz. Bu yzden genelde bu parametreye False gireriz. lpName: Eer farkl ilemler bu mutekse eriecekse bu muteksimize bir isim vermemiz gerekmektedir. Buraya nil de girebilirisiniz. Bu durumda isimsiz bir muteksimiz olur. Bu fonksiyon eer muteksi oluturabilirse kt olarak muteksin handle numarasn dnderir. Eer muteks olumam ise 0 deerini kt olarak verir. Muteksleri, kritik blgeler gibi dnebilirsiniz. Kritik blge oluturmak iin kullandmz InitializeCriticalSection fonksiyonu yerine CreateMutex fonksiyonunu, kritik blgenin balangcn belirten EnterCriticalSection yerine WaitForSingleObject fonksiyonunu, kritik blgenin bitiini bildiren LeaveCriticalSection fonksiyonu yerine de ReleaseMutex fonksiyonunu kullanyoruz. CloseHandle ile de CreateMutex ile oluan muteksin handlen kapatmay da unutmuyoruz. lk nce bir muteks oluturalm.
birmuteks := CreateMutex(nil, False, 'ornekmuteks');

Ardndan kritik blgeyi oluturduumuz yerlerdeki fonksiyonlar muteksin fonksiyonlar ile yer deitiriyoruz. imdilik sadece Kanal1 iin rnek verelim.
procedure Kanal1; var i: Integer; begin for i := 0 to 10 do begin WaitForSingleObject(birmuteks, INFINITE); //Muteksin tetiklenmesini bekle

SonucYazi := SonucYazi + Sleep(10); SonucYazi := SonucYazi + SonucYazi := SonucYazi + SonucYazi := SonucYazi + Sleep(10); SonucYazi := SonucYazi + SonucYazi := SonucYazi + SonucYazi := SonucYazi + Sleep(10); ReleaseMutex(birmuteks); end; end;

'd'; 'i'; 'y'; 'e'; 'z'; 'o'; 'n '; //Muteksi serbest brak.

imdi muteksin ne olduuna dair kafanzda bir eyler ekillenmeye balmamtr. nk kodlara baktmzda WaitForSingleObject fonksiyonu bir eyleri bekliyor ve ReleaseMutex ise bir eyi serbest brakyor. WaitForSingleObject, "tek bir nesne iin bekle" gibi bir manas var. Esas yapt grev, birinci parametresinde verilen handle numarasna gre bir muteksi beklemektir. kinci parametresinde ise ne kadar bekleyeceini belirliyoruz. Bu rneimizde INFINITE (sonsuz) girmekle, bir muteks tetiklenene kadar bekleyeceini belirttik. Bu parametreye, en fazla ne kadar bekleneceini milisaniye eklinde girebilirsiniz. Muteksi anlamada genelde verilen rnek ve bence en iyi rnek bayrak yardr. Bayrak yarlarnda bir eritte ancak bir koucu koabilir. Bayra alan komaya balar. Bir eritte, bayraa sahip olmayan, koabilmek iin bayra beklemesi gerekmektedir. Byelece her bir eritte sadece bir yar koabilir. Aynen bunun gibi, kanallar alabilmek iin mutekse sahip olmalar gerekmektedir. Mutekse sahip olan komaya yani almaya balar. Mutekse sahip olmayan, muteksin kendisine gelmesini bekler. Bir hatrlatma olarak, nceki paragraflarda sahip olmann tetiklemek olduundan bahsetmitik. te WaitForSingleObject ile muteksin bu kanalmza gelmesini beklemi oluyoruz. Ve bunu rneimizde, INFINTE yani sonsuz sre kadar bekliyoruz. Ve muteksle iimiz bittiinde dier kanallarn kullanabilmesi iin muteksi ReleaseMutex ile serbest brakyoruz. Ve yine tekrar ediyorum "Bir mutekse ayn anda sadece bir tek kanal sahip olabilir." CreateMutex ile oluturduumuz muteksin handlen formun OnDestroy olaynda CloseHandle ile kapatmay da unutmuyoruz. Yani kritik blgelerde yaptmz DeleteCriticalSection yerine
CloseHandle(birmuteks);

gibi bir ey yazmalyz. Bu rnei, makaleyi fazla uzatmasn diye, burada tm kodlarn yazmayacam. Muteks rnek projesini buradan indirebilirsiniz. Eer tek bir muteks deil de bir den fazla muteksi tetiklemesini bekelemek istersek ne olacak? Bu durumda WaitForMultipleObjects fonksiyonunu kullanacaz. WaitForSingleObject tek bir muteksi beklerken WaitForMultipleObject fonksiyonu birden fazla muteksi beklemektedir. Kullanm ayn olduu iin bununla ilgili bir rnek yapmayacam. Bu fonksiyon ile iligli tek syleyebileceim ey ikinci parametrede beklediiniz mutekslerin handlelarn dizi eklinde girmeniz olacaktr.

Baka lemlerden Mutekslere Sahip Olma

Dediimiz gibi, mantk olarak mutekslerin kritik blgelerden tek fark, baka program ve ilemlerdeki kanallar ile ortak ve e zamanl almaya izin vermesidir. Bunun iin muteksimize bir isim vermemiz gerektiini sylemitik. te bu ismi kullanarak baka bir ilem muteksin handle numarasn alabilir. Gerisinde de WaitForSingleObject ve ReleaseMutex fonksiyonlarnda bu handle numarasn kullanabiliriz. Yani mevcut bir muteksin handle numarasn alabilmek iin:
birmuteks := OpenMutex(0, False, 'ornekmuteks');

dememiz yeterli. Bunu yukardaki rneimizde CreateMutex yerine kullanabilirsiniz. Ama unutmamannz gereken nokta bunun ile mevcut bir muteksin handle numarasn alrz. Eer muteks CreateMutex ile hi oluturulmam ise bu fonksiyon 0 deerini dnderir. OpenMutex fonksiyonunun parametreleri CreateMutex ile ayn olduundan burada fazladan bir aklama yapmayacam. Bu konuda bir rnek yapmak isterseniz yukarda verdiimiz rnei iki uygulamaya blebilirsiniz. Kanal1i birinci uygulamada, Kanal2yi de ikinci uygulamada oluturursunuz. Ve ikisinde de OnCreate olaynda yle bir ey girersiniz:
var BirMuteks: THandle; ..... procedure TForm1.OnCreate(....); begin BirMuteks := OpenMutex(0, False, 'ornekmuteks'); if BirMuteks = 0 then BirMuteks := CreateMutex(nil, False, 'ornekmuteks');

Ksaca burada yaptmz ilem eer "ornekmuteks" isminde bir mutex henz oluturulmam ise oluturuyoruz. Bu ekilde iki veya daha fazla sayda ilem tek bir muteks zerinde kanallarn e zamanl olarak altrabilirler. Bu rnei kendiniz yapmaya aln. Geri burada yaplm var . Ama ltfen indirip incelemeden nce kendiniz yapmay deneyin. te size iki adet yntem. Ve ikisi de windows programlamada ok sk kullanlmaktadr. Kanallar beraber altrmak istediinizde, istrer muteks kullann isterseniz kritik blge tanmlayn. Her ikisinin de tanmlanmas ve kullanlmas ok kolay. Kafanzdaki "kanal kullanmak ok zordur" dncesini birinci blmde atnz dnyorum. Buraya kadar olan ksm ile de "kanallar e zamanl altrmak zordur" dncesini yendiinizi zannediyorum. Peki muteks ve kritik blge arasnda tercih yapmak gerekirse hangisini tercih etmeliyiz. Bence performans asndan pek fark olmaz. Seim noktanz u olmal. Eer, tek bir ilemden deil de bir ok ilemden yani bir ok programdan oluan bir gurubun kanallar ortak ve e zamanl kullanmasn dnyorsak kesinlikle muteks kullanmalyz. Aksi durumda yani e zamanl alacak olan kanallar sadece tek bir ilemde bulunuyorlarsa o zaman seim size kalm. Hangisi rahatnza gidiyorsa onu kullanabilirsiniz. imdi farkl bir konuya gei yapalm. Hazrsanz ikinci sayfadan devem edelim. Sayfalar: 1 2

Delphi ile Thread(Kanal) Kullanm Blm 2


Posted on 06 Ekim 2007 | 19 Yorum

VCL ve Kanallar
VCL ktphanesini normalde tamamen kanal uyumlu deildir. Daha dorusu bir ka nemli ey dnda VCL, kanal uyumlu olarak gelitirilmemitir. Byle olmasnn sebebi, tamamen performans ile ilgilidir. Eer VCL tamamen kanal uyumlu olsayd ok yava alrd. Ama u an VCL ktphanesindeki bileenler gerekten hzldr. Eer VCL tamamen kanal uyumlu yaplsa idi bir ok yerde kritik blge ve muteksler oluturulacakt. Bu kritik blge ve mutekslerin says o kadar ok olacakt ki VCLin performans farkedilir derece decekti. Bu yzden VCLe kanal uyumluluu katmak programcnn sorumluluuna braklmtr. VCLde bulunan ve genelde TControlden treyen grlebilen bileenleri kanallar ile kullanmaya kalkmak ou kez kmelere ve imelere sebep olacaktr. Buraya kadar rendiimiz teknikler ile VCL bileenlerini, zellikle grlebilen bileenleri kullanmaya kalktnzda hemen kendini belli etmese bile eninde sonunda sorunlar karacaktr. Bu yzden VCL ile kanallar kullanabilmek iin yine VCL iinden bir aremiz bulunmakta. Bu are TThread snfdr. Eer birinci blmn balarn hatrlarsanz bir uyarda bulunmutum. Bu uyary bir daha hatrlatyorum. Eer yapacanz ilemler VCLi kullanmay gerektiriyorsa, daha dorusu TComponentin alt snfndaki bileenleri kullanmay gerektiriyorsa TThread snfn kullanmamz gerekmetedir. Bunun nedenini ileriki konularda syleyeceimizi yazmtk. te bunun nedenini bu baln ilk paragraflarnda bahsettik ve imdi de bahsetmeye devam ediyoruz. Peki TThread snf ne yapyor? TThread snf bize kanallardan VCL armlar yapabilmek iin Sycnhronize metodunu salyor. En byk avanataj da budur. VCL ktphanesi, zellikle TComponentin alt snflar programn ana kanalnn bir paras olmak zere gelitirilmilerdir. Bu yzden VCLi kanallardan kullanmak genelde felaketler ile sonulanmtr (ff ok mu ar oldu ne ). Synchronize metodunun getirdii zm ise ok basittir. Bir an iin mevcut kanaln ilemesini durduruyor ve bu metod ile yapacamz ilemleri ana kanaln bir paras haline getiriyor. Bu ilemler bittiinde ise mecut kanal iine devam ediyor. Bylece bu metod ile VCLi, ana kanal ile e zamanl olarak baka kanallardan kullanabiliyoruz. Bu yzdendirki bu metodun ismi Synchronize olmutur, yani anakanal ile "e zamanl yap". Ksacas, VCLe ulaabilmek iin ilemlerimiz ana kanaln bir paras olmak durumunda. VCLe ulamadmz zamanlarda ise e zamanl almadan normal kanal yapsna dnmeliyiz. te btn bu ilemleri Synchronize metodu sizin yerinize gerekletirmektedir.

Dedik ki genelde TComponentin alt snflar kanal uyumlu deildir bu yzden TThread kullanmalsnz. Bununla birlikte baz TComponentden tremeyen snflar rahat bir ekilde kanal kullanlabilir. Bunlardan bazlar unlardr. TStreamden treyen snflar, TStringsden treyen snflar, TList snf, TComponentin kendisi, vs.. Evet TComponentin kendisini de kanallarla beraber kullanabilirsiniz. Bunun bize salayaca yle bir fayda vardr. TThread nesnesini kullanmak yerine, tamamen kanal uyumlu bileen yapabilirsiniz. Tabi gerekten ne yaptnz biliyorsanz ortaya tamamen kanal uyumlu ve TComponentden tremi bileenler oluturabilirsiniz. Bunun iin nerelerde kritik blge ve muteks oluturacanz iyi bilmelisiniz. Yani kanal uyumlu bir bileen yapmann yolu kritik blgeleri ve muteksleri akllca kullanmaktan geer. Bu ekilde bileenlerinizi tam kanal uyumlu yapabilirsiniz. Her neyse Bu paragraf daha ok bileen yazarlarn ilgilendiriyor. imdi TThread nesnesinin kullanmn basit ve soyut bir rnek ile grp geeceiz ve ardndan gerekte en ok karlalan problemlerden, kanal uyumlu veritaban eriimi ile iligli bir rnek daha yapacaz.

TThread Nesnesinin Kullanm


Yapacamz ilem ok basit olacak. Sadece TThread nasl kullanlr bunu inceleyeceiz. Bunun iin ilk bata yeni bir uygulama aalm ve forma bir adet progressbar ekleyelim. Progressbar bileeninin Step zelliine 1 deerini verelim. Ardndan bir adet button yerletirelim ve Caption zelliini "Kanal altr" eklinde deitirelim. File / New mensnden "Other" seeneini seelim. "Delphi Files" sekmesinden "Thread Object"i bulup ift tklayalm. Eer sizin Delphi srmnzde byle bir seenek yok ise siz yeni bir unit oluturun ve birazdan vereceimiz kodlar direk olarak yaptrn ve kaydedin. Eer varsa, karnza TThread nesnesinden treyecek olan bir snf ismi soracaktr. Bu rneimiz iin "Class Name" olarak TBirDonguKanali yazalm ve entere basalm. Ve sonrasnda alan uniti aadaki gibi deitirelim:
uses SysUtils, Classes; type TBirDonguKanali = class(TThread) protected procedure ProgressGuncelle; procedure Execute; override; end; implementation { TBirDonguKanali } uses Unit1; procedure TBirDonguKanali.Execute; var i: Integer; begin for i := 0 to 99 do begin

Sleep(100); Synchronize(ProgressGuncelle); end; end; procedure TBirDonguKanali.ProgressGuncelle; begin Form1.ProgressBar1.StepIt; end;

Buradaki "Unit1", formumuzun uniti. imdi de formdaki dmemizin OnClick olayna unlar yazalm:
procedure TForm1.Button1Click(Sender: TObject); var kanal: TBirDonguKanali; begin kanal := TBirDonguKanali.Create(False); end;

Tabiki TBirDonguKanali snfn kullanabilmek iin formun bulunduu unitin uses ksmna threadin untini eklemeliyiz. imdi bu program derleyelim ve altralm. Dmeye basmadan nce her zamanki gibi ak deilse "Thread Status" ve "Event Log" pencerelerini grebileceimiz bir yere koyalm.. Ardndan dmeye basalm ve olanlar gzlemleyelim. Aslnda yaptmz basit bir kanal tanmlamasndan baka bir ey olmad. Fakat bir noktada ok farkl bir arm yaptk. O da Synchronize arsdr. Synchronize metodu, bir adet parametre almaktadr. Bu parametre, programn ana kanal ile e zamanl altracamz ilemleri iieren bir prosedr olacaktr. Bu prosedr parametresiz ve bir snfn nesnesi olmak zorundadr. Bu rneimizde ProgressGuncelle metodu bu ii grmektedir. Bu yzden Synchronize metodu ile bu metodumuzu e zamanl yaptk. Yine hatrlatmak gerekirse Sleep runtinin kodlar yavalatmak iin kullandk. Aksi durumda ilemler ok hzl olur ve ne olup ne bitiyor gremeyiz. Ama siz kendi programlarnzda bunu kullanmak zorunda deilsiniz. Burada bir karara vardk. ProgressBarn adm artrlacak ve ekrana tekrar izilerek gncelenecek. Bu ii yapan StepIt metodunu armamz gerekir. te bu durumda aldmz karara gre bu metodu Synchronize ile e zamanl hale getirmemiz gerklidir. nk VCLe ait ProgressBar bileenine burada mdahale ediyoruz. Grdnz gibi Execute metodunu TThreadden miras alp zerine yazyoruz. Ve ierisine kanal altrldktan sonra yaplacak kodlar yazyoruz. Ardndan VCL kullanlan ksmlar belirleyip Synchronize ile sarmalyoruz. Kanalmz dmenin OnClick olayn olutururken yle bir ey yazmtk.
kanal := TBirDonguKanali.Create(False);

Burada TThread snf bir adet parametre alyor. Bu parametreye False girdiimizde kanal oluturulur oluturulmaz almaya yani Execute metodu arlmaya balanacaktr. Eer buraya True girerseniz, kanal hemen altrlmayacak ve sizin Resume metodunu armanz bekleyecektir. Yani:
kanal.Resume;

Olaki kanal durdurmak istediniz. Bu durumda aadaki metodu aryoruz.


kanal.Terminate;

Kanal ister Terminate metodu ile sonlansn, isterse kendi kendine sonlansn, TThreade ait OnTerminate olay tetiklenecektir. Yani kanal iini bitirir bitirmez bir eyler yapmak istiyorsanz yle bir eyler yapmalsnz.
TForm1 = class(TForm) ..... ..... procedure KanalDurdu(Sender: TObject); .... ... kanal.OnTerminate := KanalDurdu; ..... .... procedure KanalDurdu(Sender: TObject); begin ShowMessage('Kanal sonland...'); end;

gibi Bu basit bir rnekti. Ama TThreadin alma mantn anlamak iin iyi bir rnekti diye dnyorum. imdi biraz gerek hayattan rnek verelim.

Kanal Uyumlu Veritaban Kullanm


Burada bilmeniz ve aklnza kazmanz gereken nokta udur. Her veritaban ktphanesi thread-safe yani kanal uyumlu deildir. Bunu internette hangisinin thread-safe olup olmadn aratrarak bulabilirsiniz. Delphi 6 yeni kt sralarda veritaban ve kanallarn birlikte kullanlmas noktasnda bir almam olmamt. Ve Delphi 6dan sonra DBExpress, Delphicilerin gzdesi olmaya balamt. Ben de nedenini merak etmitim. nk BDE varken ve kullanm kolayken neden DBExpress kullanlr ki? Tabi i yle basit olmadn sonralar grdm. Delphi 6 ile beraber sadece DBExpress srcleri thread-safe yani kanal uyumlu olmutu. Delphi 7 ile beraber DBExpress bileenleri ile beraber tamamen kanal uyumlu olmutur.

IBX bileenleri de thread-safe yani kanal uyumludur. Bu yzden bu iki veritabann rahat bir ekilde bu blmn balarnda anlattmz gibi kritik blge ve muteksler iinde kullanabilirsiniz. Ama BDE yani Borland Database Engine ise thread-safe deildir. Hem srcleri hem de bileenleri thread-safe deildir. Yani TDatabase, TQuery, TTable bileenleri kanal uyumlu deildir. Demek ki veritabanlar iin sadece bileenlerin kanal uyumlu olmas yetmiyormu. Veritaban srcleri de kanal uyumlu olmal imi. Piyasada bir ok veritaban ve bir ok veritaban bileeni mevcut. Burada kalkp hepsini teker teker burada tartmamz biraz zor olur. Bu yzden burada sadece BDE zerine rnek vermek istiyorum. Zira BDE bileenleri kanal uyumlu olmadndan kanallar, programclar en ok BDE bileenlerinde zora sokmakatadr. BDE kanal uyumlu deildir, fakat TSession bileeni ile kanal uyumlu hale getirilebilir. TSession ile her farkl ileminiz iin farkl bir oturum aarsnz. Fakat dikkat edilmesi gereken bir ka nokta var. Birincisi, her bir sorgu iin ilikilendirilmi bir oturum olmaldr. Bu ilikilendirme dataset bileeninde(TQuery, TTable, vs..) bulunan SessionName zellii ile yaplr. Ayn ekilde TDatabase kullanyorsanz her bir TSession iin ayr ayr TDatabase bulunmaldr. kincisi, bir dataset bileeni kullanld kanalda bir DataSourcea balanamaz. Bunu yapabilmek iin TThreadde bulunan Synchronize metodunu kullanmalsnz ya da bu balama ilemini ana kanalda yapmalsnz. Bu iki noktaya dikkat ettiiniz srece BDE ile sorununuz olacan zannetmiyorum. Genelde BDEnin thread-safe olmad bilinmiyor veya unutuluyor ve ayr ayr kanallarda SQL cmlecikleri altrlmaya kalklyor. Halbuki BDE ve bileenleri bunu desteklemezler. Ksaca bir daha vurgulamak istiyorum. Birincisi, her kanal iin bir TSession ayarlamalyz ve kanallarda kullanlacak olan her dataset bileeni iin kanala ait TSessionn, SessionName zellii ile atamalyz. kincisi, dataset bileenlerini hi bir DataSource nesnesine balanmadndan emin olmalsnz. nk bu balama ilemi Synchronize ile yaplmaldr. imdi bununla iligli bir rnek yapalm. Uygulamamzda ayn anda iki adet sorgu altrlsn ve sonucu ekranda gstersin. Yeni bir uygulama aalm ve form zerine iki adet DBGrid, bir adet DataSource, iki adet TQuery, iki adet TSession ve bir adet button koyalm. ki adet dbgridin DataSource zelliini eklediimiz DataSource olarak belirleyelim. Query bileenlerinin DatabaseName zelliini DBDEMOS yapalm. Ve birinci query bileenine aadaki sql kodunu yazalm.
SELECT * FROM customer

kincisine de aadaki sql cmleciini yazalm


SELECT * FROM country

lk bata oturum bilgilerini ayarlayalm. Bunun iin iki adet eklediimiz TSession bileenlerinden birincisinin SessionName zelliine "Oturum1" dierine de "Oturum2" diyelim. Ardndan iki adet eklediimiz query bileenlerinden birincisinin SessionName zelliini Oturum1, dierini de Oturum2 olarak ayarlayalm. Bylece bahsettiimiz uyarlardan birincisini halletmi olduk. imdi ikinci uyary hatrlyoruz. Asla bu dataset bileenlerine yani bu rneimizdeki query bileenlerine DataSource atamyoruz. nk bunlar Synchronize ile balanmas icab ediyor. imdi ikinci uyar eliinde bir kanal oluturalm. Ya kendiniz elinizle oluturun ya da bir nceki rnekte anlattmz gibi File / New mensnden "Thread Object" ile bir TThread kanal oluturun. TThread snfmza TSorguKanali ismini verelim. Bu snfn gvdesini aadaki gibi dzenleyelim.
uses SysUtils, Classes, Forms, Db, DbTables; type TSorguKanali = class(TThread) private FDataSource: TDataSource; FDataSet: TDataSet; FSorguHatasi: Exception; protected procedure DataSourceBagla; procedure SorguHatasiGoster; procedure Execute; override; public constructor Create(DataSource: TDataSource; DataSet: TDataSet); virtual; end;

Grdnz gibi bir constructor tanmladk. nk bu kanalmz hangi datasete hangi datasourceun balanacan bilmesi art. imdi ise bu snfn kod tanmlamalarn verelim.
constructor TSorguKanali.Create(DataSource: TDataSource; DataSet: TDataSet); begin inherited Create(True); FDataSource := DataSource; FDataSet := DataSet; Resume; end;

lk bata Create constructorna bakalm. inherited ile ata snfmz olan TThreadin Createini aryoruz. Tabi burada True parametresi ile aryoruz. Yani kanalmz oluturulur oluturulmaz almaya balamayacak! Bizim Resume metodunu kullanmamz bekleyecek. nk imdi almaya balarsa FDataSource ve FDataSet deikenleri atanmam bir ekilde Execute metodu altrlacak. Bu da FDataSet ve FDataSource tanml olmadndan hataya sebep olacaktr. Bu yzden kanal durgun bir vaziyette oluturuluyor. Ardndan FDataSource ve FDataSet deikenlerine gelen parametreleri atyoruz. Bylece

artk Execute metodunu altrabiliriz. Bunun iin durmu durumda olan kanalmz Resume ile altrmaya balyoruz.
procedure TSorguKanali.Execute; begin try FDataSet.Open; //Session iinde olduundan syncronize'a gerek yok. Synchronize(DataSourceBagla); // BURAYA DKKAT!! except FSorguHatasi := ExceptObject as Exception; Synchronize(SorguHatasiGoster); // BURAYA DKKAT!! end; end;

Execute metodumuzda DataSource balama ilemini yapyoruz. lk bata DataSet bileenimizi ayoruz. DataSet bileenine bir session atadmzdan rahatlkla Open ile queryde yazdmz sorgu altrlabilir. Ama DataSource bal olmadnda u an dbgridlerde bir ey gsterilmez. Ardndan dbgridlerde veya datasourcea bal hangi db bileenleri varsa onlar harekete geirip formda bireyler gstermesini salamak iin DataSet bileenimize DataSource bileenini balamalyz. Uyarlardan ikincisini hatrlarsanz bu balama ilemi Synchronize ile yaplmal. Bu yzden burada Synchronize ile balama iini yapan metodumuzu aryoruz. Sonrasnda gelen kodlar bir sql hatas olmas durumunda hatann gsterilebilmesi iindir. te burada da hatay gsterirken Synchronize kullanmalyz. nk ekranda gsterilecek olan mesaj VCLin grsel ksm ile ilgilidir ve Synchronize yaplmaldr.
procedure TSorguKanali.DataSourceBagla; begin FDataSource.DataSet := FDataSet; end; procedure TSorguKanali.SorguHatasiGoster; begin Application.ShowException(FSorguHatasi); end;

Bu iki metod, Synchronize yaptmz metodlardr. Birincisinde DataSet bileenimize DataSourceu balyoruz. kincisinde ise ekranda hata mesajn gsteriyoruz. Kanalmz hazr durumda. Dilerseniz imdi bu kanal kullanarak formumuzdaki queryleri kanallarda altralm. Bunun iin formumuza geri dnelim ve Buttonun OnClick olayna u kodlar yerletirelim.
procedure TForm1.Button1Click(Sender: TObject); var Kanal1, Kanal2: TSorguKanali; begin Kanal1 := TSorguKanali.Create(DataSource1, Query1); Kanal2 := TSorguKanali.Create(DataSource1, Query2); end;

Program altrdnzda iki adet sorgunun ayn anda(kullancya gre) altrldn greceksiniz. Bu rnekten karmamz gereken en byk sonu, Synchronize armnn yaplaca yer ve zamanlar iyi tespit edebilmektir. Bu armn nerelerde kullanlacan bildiiniz taktirde hi bir sorunla karlaacanz zannetmiyorum. ahsen genelde VCLin grnm ile ilgili ksmlarnda ve TComponentde treyen ve threadsafe olmadn bildiim bileenler iin Synchronize kullanyorum. Buraya kadar geldi iseniz, kanallar noktasnda ok fazla yol katettiniz demektir. Aslnda makaleyi burada bitirebiliriz. Ama fazladan bir ka kk konuya daha deinmek istiyorum.

Kanallarn nceliini Ayarlamak


Kanallarn nceliinin, kanaln alma sresini etkilediini biliyoruz. Bir kanaln nceliini belirlemek iin SetThreadPriority fonksiyonunu kullanyoruz. Kanaln nceliini renmek iin ise GetThreadPriority kullanlr. Kanallarn, ilemler iinde bulunduunu biliyoruz. Bir kanala bir ncelik atamanz o ilem iin geerli olacaktr. Daha dorusu kanaln iinde bulunduu ilemin ncelii direk olarak kanaln nceliini de etkilemektedir. Bu durumda iki adet ncelik kavram ortaya kyor. Birincisi ilemin ncelii ikincisi kanaln nceliidir. Eer ilem nceliiniz ok yksek ise, ilemin iinde bulunan tm kanallarn ncelikleri de o lde yksek olur. Bir ilemin nceliini belirlemek iin ise SetPriorityClass fonksiyonunu kullanyoruz. Ayn ekilde GetPriorityClass ile de ilemin mevcut nceliini renebiliriz. Bir kanala vereceiniz ncelik deerleri THREAD_PRIORITY ile balar. SDK yardm dosyalarndan tm listeyi bulabilirsiniz. Ya da Delphi editrnde THREAD_PRIORITY yazp ctrl+boluk yapabilirsiniz. SetThreadPriority aadaki gibi tanmlanmtr.
function SetThreadPriority(hThread: THandle; nPriority: Integer): BOOL; stdcall;

lk parametreye nceliini belirlemek istediimiz kanaln handle numarasn giriyoruz. kinci parametrede ise THREAD_PRIORTY ile balayan seeneklerden birini giriyoruz. Mesela aadaki rnek, kanalmz idle yani hi bir ilem yaplmazken altrmaya balayacaktr. Ayrca idle, en dk ncelii ifade eder.
handle := CreateThread(.....); SetThreadPriority(handle, THREAD_PRIORTY_IDLE);

gibi

Kanallar Durdurmak ve ldrmek


Aslnda bu konudan bahsettik. Kanallar durdurmak iin ResumeThread fonksiyonu kullanlr. ldrmek iin ise TerminateThread ile hafzadan silinir. Ama bununla ilgili bir rnek yapmamtk. Aada bir kanal durgun halde balatlyor, sonra altrlyor ve hemen ardndan durduruluyor. En sonda ise hafzdan siliniyor.
//Kanal CREATE_SUSPENDED ile durgun oluturuluyor. handle := CreateThread(nil, 0, @ThreadFunc1, nil, CREATE_SUSPENDED, ThreadID1); ResumeThread(handle); //Kanal altr SuspendThread(handle); //Kanal durdur TerminateThread(handle, 0); //Kanal ldr

Ayn eyi TThread ile yapalm.


//TBirKanal snf TThread snfndan trediini varsayalm. kanal := TBirKanal.Create(True); //Durgun olutur. kanal.Resume; //altr kanal.Suspend; //durdur kanal.Terminate; //ldr.

Sonu
Grdnz gibi kanallarla uramak o kadar da zor bir hadise deil. Sadece neyin nerede kullanlacan iyi bilmeilisiniz. Kanallar hakknda "kanal kullanmak ustalarn iidir, bize gelmez" gibi bir n yargy kafanzdan attnz dnyorum. Zira artk windowsun ileri seviye konularndan olan kanal oluturma, kritik blge tanmlama ve muteks kullanmn grdnz. Belki de dikkat etmeniz gereken tek nokta VCL konusu. Ama burada bahsettiiimiz gibi her ne kadar VCL kanal uyumlu olmasa da TThread gibi bir zm de bulunmakta. Bundan sonras size kalm. imdi enmeyin ve programlarnz kanal uyumlu yapn. Kanallar ne kadar ok kullanrsanz, o kadar ok alrsnz. Her zamanki gibi yorum ve eletirileriniz bekliyorum. Sorularnz olduunda buradan ya da delphiturkiye forumlarndan iletebilirsiniz. Fatih Tolga Ata 2007

Kaynaklar

Windows SDK Yardm Dosyalar CodeGear Rad Studio Yardm Dosyalar Classes.pas Dosyas Delphi 4 Unleashed, Charlie Calvert, Sams Pub., 1999 borland.delphi.* haber guruplar

Sayfalar: 1 2 stad aada ekilde ib bileenlerinden netice aldm fakat VCL ile ib bileenlerinde neticeye ulaamadm VCL ile IB bileenlerine rnek verebilirsen sevinirim. Ben 1000000 kayt zerine 2 query ile balanarak test yaptm
procedure Sorgu1; procedure Sorgu2; var Form1: TForm1; BirinciKritikBolge: TRTLCriticalSection; //kritik blgemizi temsil ediyor. implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var // BirinciQuery:TSorguKanali; Kanal1ID:DWord; begin // BirinciQuery := TSorguKanali.Create(TIbDataset(IBQuery1), Datasource1); CreateThread(nil, 0, @Sorgu1, nil, 0, Kanal1ID); ShowMessage('1'); end; procedure TForm1.Button2Click(Sender: TObject); var // IkinciQuery:TSorguKanali; Kanal2ID:DWord; begin // IkinciQuery := TSorguKanali.Create(TIbDataset(IBQuery2), Datasource2); CreateThread(nil, 0, @Sorgu2, nil, 0, Kanal2ID); ShowMessage('2'); end; procedure Sorgu1; begin EnterCriticalSection(BirinciKritikBolge); //BirinciKritikBolge balangc Form1.IBQuery1.Open; // Form1.DataSource1.DataSet := Form1.IBQuery1; Form1.IBQuery1.FetchAll; LeaveCriticalSection(BirinciKritikBolge); //BirinciKritikBolge bitii end; procedure Sorgu2; begin EnterCriticalSection(BirinciKritikBolge); //BirinciKritikBolge balangc Form1.IBQuery2.Open; // Form1.DataSource2.DataSet := Form1.IBQuery2; Form1.IBQuery2.FetchAll; LeaveCriticalSection(BirinciKritikBolge); //BirinciKritikBolge bitii end; procedure TForm1.FormCreate(Sender: TObject); begin

InitializeCriticalSection(BirinciKritikBolge); end; procedure TForm1.FormDestroy(Sender: TObject); begin DeleteCriticalSection(BirinciKritikBolge); end; end.[/delphi]

Bu duruma gre deiir. Eer kritik blge kullanmanz gerekiyorsa bunu tanmlamalsnz. Mesela okuma ve yazma ilemlerinizde tekillik olmas sz konusu ise yani, ayn anda ayn veriye yazma ilemi yaplmas hata meydana getirecekse kritik blge tanmlamalsnz.

You might also like