<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Delphi Programming</title>
	<atom:link href="http://www.tugrulhelvaci.com/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://www.tugrulhelvaci.com</link>
	<description>Delphi dilinin incelikleri ve işletim sistemi ile etkileşimini izah etmeye çalıştığım kişisel bir blogdur.</description>
	<lastBuildDate>Sun, 13 Jun 2010 03:53:23 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Yeni Veri Tipleri ve Operator Overloading</title>
		<link>http://www.tugrulhelvaci.com/?p=599</link>
		<comments>http://www.tugrulhelvaci.com/?p=599#comments</comments>
		<pubDate>Sun, 13 Jun 2010 03:10:01 +0000</pubDate>
		<dc:creator>Tuğrul HELVACI</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Operator Overloading]]></category>

		<guid isPermaLink="false">http://www.tugrulhelvaci.com/?p=599</guid>
		<description><![CDATA[ Bir önceki makalemizde Interface&#8217;ler ile ilgili bilgi vermiş ve Operator Overloading namı diğer operatör aşırı yükleme hususuna değineceğimizden bahis açmıştık. Bu makalemizde; Operator Overloading hakkında yazacağız ve .Net framework&#8217;te olduğu gibi çalışan yeni bir String ve DateTime veri türü oluşturacağız. Ancak öncelikle, aslında hepimizin bildiği operatör kavramına bir göz gezdirmekte fayda var sanırım.
 Operatörler; [...]]]></description>
			<content:encoded><![CDATA[<p> Bir <a href="http://www.tugrulhelvaci.com/?p=582">önceki makalemizde</a> Interface&#8217;ler ile ilgili bilgi vermiş ve <span style="color: #ff9900;">Operator Overloading</span> namı diğer operatör aşırı yükleme hususuna değineceğimizden bahis açmıştık. Bu makalemizde; Operator Overloading hakkında yazacağız ve .Net framework&#8217;te olduğu gibi çalışan yeni bir <span style="color: #ff9900;">String ve DateTime</span> veri türü oluşturacağız. Ancak öncelikle, aslında hepimizin bildiği operatör kavramına bir göz gezdirmekte fayda var sanırım.</p>
<p> Operatörler; belirli veri türleri arasında ilişki kuran, onları bir sonuca ulaştıran yapılardır. Hemen hemen hergün kullandığımız aritmetik işlemlerdeki toplama(+), çıkartma(-), çarpma(*) ve bölme(/) işlemlerini ifade ettiğimiz simgeler; yada mantıksal işlemlerde kullandığımız or(veya), and(ve), not(değil) vb. gibi simgesel ifadelere operatörler denilebilir. Bildiğiniz gibi; operatörler veri türleri üzerinde üstlerine düşen mantıksal yada matematiksel işlemleri yaparak bir sonucun üretilmesini sağlarlar. Operatörlerin bu vazifelerini, gerek günlük yaşantımızda; gerekse de programlama hayatımızda sıklıkla kullanırız. Programlama ortamlarında yaşadığımız tecrübeler gereği operatörlerimizi düzgün veritipleri ile kullanırız. Örneğin toplama operatörü; sağındaki ve solundaki verilerin toplanması amacına hizmet eder. Ancak; toplama operatörünün sol ve sağındaki değerlerin sayısal olması yada alfasayısal olması durumlarında toplamanın neticesi farklılık arzeder. Basit bir örnek verelim;</p>
<pre class="brush: delphi">
var
  iLeft, iRight, iTotal : Integer;
  sLeft, sRight, sTotal : String;
begin
  iLeft := 10;
  iRight := 20;
  iTotal := iLeft + iRight;

  sLeft := &#039;Operator &#039;;
  sRight := &#039;Overloading&#039;;
  sTotal := sLeft + sRight;
end;
</pre>
<p> Yukarıda gördüğünüz basit örnekte, aynı operatör(+) farklı sonuçlara hizmet etmektedir. Birinci durumda sonucumuz &#8220;30&#8243; olurken; ikinci durumda sonuç &#8220;Operator Overloading&#8221; olmaktadır. Hemen hemen hiçbirimiz; iki farklı veri türünü aynı operatöre vermeyiz. Örneğin hiçbirimiz; bir sayı ile bir string ifadeyi toplamayız. Ancak bazı durumlarda, bir veri türü ile bir başka veri türünü herhangi bir operatöre vererek işlemek isteyebiliriz. Bu gibi durumlarda, genellikle derleyicimiz bizleri uyaracak bir warning vermekle yetinir. Ancak bazı durumlarda yaptığımız işlemlerde beklediğimizin dışında sonuçlar gördüğümüz de olur. Örneğin;</p>
<pre class="brush: delphi">
var
  bLeft, bRight, bTotal : Byte;
  wLeft,wRight,wTotal : Word;
begin
  bLeft := High(Byte); // 255
  bRight:= High(Byte); // 255
  bTotal:= bLeft + bRight; // ??

  wLeft := High(Word); // 65.535
  wRight:= High(Word); // 65.535
  wTotal:= wLeft + wRight; // ??
end;
</pre>
<p><span id="more-599"></span><br />
 Yukarıdaki örneğimizde 3 adet <span style="color: #ff9900;">Byte</span> ve <span style="color: #ff9900;">Word</span> değişken tanımlanıyor ve bu değişkenlerden Left ve Right olanlara o değişken tipinin alabileceği maksimum değer atanıyor. Ardından bu değişkenler birbirleri ile toplanıyor. Bu durumda Byte değişkenlerin toplanmasından elde edilecek değerin; 255 + 255 = 510 olması ve Word değişkenlerin toplanmasından elde edilecek değerin de 65.535 + 65.535 = 131.070 olması bekleniyor. Ancak durum tamamen bunun dışında sonuçlanıyor. Byte değerlerin toplanmasından elde edilen sonucun <span style="color: #ff9900;">254</span> olduğunu ve Word değerlerin toplanmasından elde edilen sonucun ise; <span style="color: #ff9900;">65.534</span> olduğunu görüyoruz.</p>
<p> Eminim buna benzer sıkıntılara daha önceleri rastlamışsınızdır. Bu gibi sorunlar tespit edilmesi son derece güç sorunlar olduğu için bu hususa da değinmeden geçmek istemedim. Bu garip durumun aslında son derece mantıklı bir açıklaması var. Bildiğiniz gibi bir Byte veri türü; içinde en yüksek değer olarak 255 sayısını tutabilir. Byte türündeki bir değişkene 255 + 255 &#8216;lik bir verinin atanmaya çalışılması durumunda; 510 olması gereken veri kırpılarak atanır. Kırpılma işlemi; ilgili sonucun 255 + 1 &#8216;e göre mod alınmasından kalan değerdir. Byte ve Word değişkenlerimizdeki durum aşağıdaki gibi olur;</p>
<pre class="brush: delphi">
var
  bLeft, bRight, bTotal : Byte;
  wLeft, wRight, wTotal : Word;
begin
  bLeft := High(Byte); // 255
  bRight := High(Byte); // 255
  bTotal := bLeft + bRight; // İşte tam bu noktada; 510 mod (255 + 1) işlemi yapılıyor. Bu da 254 sonucunu verecektir.

  wLeft := High(Word); // 65.535
  wRight := High(Word); // 65.535
  wTotal := wLeft + wRight; // Bu noktada da; 131.070 mod (65.535 + 1) işlemi yapılıyor. Bu işlemin sonucu da 65.534 sonucunu verecektir.
end;
</pre>
<p> Gördüğünüz gibi; yaptığımız aritmetik işlemlerde artı operatörünün her iki tarafındaki veri türü ve sonucun atanacağı veri türüde birbirine eşit olmasına rağmen beklenen sonucun aksi bir durum ile karşılaştık. Buna benzer bir başka garip durumu daha sizlerle paylaşmak isterim. Aşağıdaki örneği lütfen dikkatlice inceleyiniz:</p>
<pre class="brush: delphi">
function Replicate(const Value : String; const Count : Cardinal) : String;
var
  Counter : Cardinal;
begin
  Result := Value;
  for Counter := 0 to Count - 1 do Result := Result + Value;
end;
..
..
..
var
  sRetValue : String;
begin
  sRetValue := Replicate(&#039;Merhaba &#039;, 0);
  ShowMessage(sRetValue);
end;
</pre>
<p> Yukarıda örneğini verdiğimiz kodda, Replicate metodunun içindeki döngünün asla çalışmayacağını düşünerek(0&#8242;dan -1&#8242;e) metodumuzun sadece &#8220;Merhaba&#8221; döndürmesini bekleriz. Oysaki durum burada da farklıdır. Örneğimizi çalıştırıp denerseniz, kodun asla ShowMessage satırına inemediğini göreceksiniz. Tabii uzun bir sabrınız yok ise. Peki burada ne gibi bir gariplik olmuştur ?</p>
<p> Dikkat ederseniz eğer, Replicate metoduna geçilen Count parametresi <span style="color: #ff9900;">Cardinal</span> veri tipinden tanımlanmıştır. Ve bu veritipi, Low(Cardinal)..High(Cardinal) aralığında yani 0 ila 4.294.967.295 arasında değer alacaktır. Veri aralığından gözlemleyeceğiniz gibi, bu veri türü negatif değerler alamamaktadır. Oysaki Replicate(&#8216;Merhaba&#8217;, 0) kullanımı sayıyı negatif aralığa sürüklemek isteyecektir(0-1). Bu gibi bir durumda; döngümüz sıfırdan 4.294.967.295 değerine kadar dönmeye çalışacaktır. Gördüğünüz gibi <span style="color: #ff9900;">pozitif sayı aralığındaki değişkenlere negatif değer atama çabaları, veri tipinin alabileceği maximum değere set edilmesini sağlar.</span></p>
<p> Bu sebeple; veri tipleri üzerinde aritmatiksel yada mantıksal operatörleri kullanırken dikkatli olmakta fayda vardır. Takdir edersiniz ki; bu tip hataları tespit etmek son derece zordur. Bu sebeple kodlarımızı yazarken bu hususları göz önünde bulundurmak programlarımızın stabilitesi ve bizim akıl sağlığımız açısından son derece önemlidir.</p>
<p> Bahsedilen bu iki garip durum senaryosunun aslında Operator Overloading hususu ile direkt bir ilgisi yok. Ancak; bu konuyu anlatırken hatalara neden olabilecek bu dikkat edilesi hususu da ilginize sunmak istedim.</p>
<p> Çevresinde dolaştığımız ancak henüz kendisine temasta bulunmadığımız Operator Overloading konusunda da biraz konuşmanın zamanı geldi sanırım. Operatörleri aşırı yüklemek; <span style="color: #ff9900;">kısaca derleyiciye ilgili işlemin sonuçlarını &#8220;ben yöneteceğim&#8221; demektir!</span> Bu iddialı sözü, genellikle programlarımızı daha da basitleştirebilmek adına söyleriz. Sıklıkla başka bir kullanım amacına rastlanmaz. Ancak; biz makalemizde Operator Overloading&#8217;i Delphi&#8217;ye yeni tipler kazandırmak amacı ile kullanacağız.</p>
<p> .Net dillerinde; Operator Overloading hem sınıflara hemde struct denilen Delphi&#8217;de record&#8217;a karşılık gelen yapılara uygulanabilmektedir. Ancak; Delphi&#8217;de Garbage Collection gibi bir çöp toplama mekanizmasının var olmaması nedeni ile <span style="color: #ff9900;">henüz bu özellik sadece record&#8217;lar ile sınırlıdır.</span> Yeni veritürlerimizden <span style="color: #ff9900;">NewString</span> türüne girmeden evvel, string türlerle ilgili bazı örnekler vermeye çalışalım:</p>
<pre class="brush: delphi">
var
  sVal : String;
  iLen : Integer;
begin
  sVal := &#039;Merhaba &#039;;
  sVal := sVal * 3; // Geçersiz

  iLen := sVal.Length; // Geçersiz
  sVal := sVal.Replace(&#039;a&#039;, &#039;x&#039;); // Geçersiz
  sVal := -sVal; // Geçersiz
end;
</pre>
<p> Örneğimiz de; sVal string türünde bir değişken tanımlanmış ardından bu değişkene &#8220;Merhaba &#8221; ifadesi atanmıştır. Ardından hepimize saçma görünebilecek bir işlem ile bir string değişken 3 ile çarpılmaya çalışılmıştır. Burada amaçlanan 3 adet &#8220;Merhaba &#8221; string&#8217;inin yanyana getirilmesidir. Ancak derleyicimiz bize; <span style="color: #ff9900;">&#8220;Incompatible types: string and Integer&#8221;</span> gibi bir hata ile yanıt vermiştir. </p>
<p> Ardından, sVal değişkenimiz üzerinde Length isimli bir metodun çağrılması istenmiştir. Bu da geçersiz bir kullanımdır. Çünkü string veri türü; Delphi&#8217;de bir sınıf yada bir record değildir dolayısı ile metod çağrımlarını desteklemez. Buna benzer bir şekilde Replace metodu da çalışmayacaktır. Son kullanım tarzımızda sVal değişkenimizin içindeki değerin ters çevrilmesi(Reverse) arzulanmıştır. Ancak şu anki imkanlarımız ile bu da mümkün değildir.</p>
<p>Tüm bu işlemleri yapabilmek için elbetteki pek çok string metodu bulunmaktadır. Ancak biz bu metodları hatırımızda tutmak yerine .Net framework&#8217;teki mantalitede kullanabilmek istersek ne yapmalıyız ? İşte tam bu noktada Record&#8217;lar ve operator overloading konusuna girmiş bulunuyoruz. Daha fazla ilerlemeden önce; Delphi&#8217;de tanımlı olan operatörlerimizin neler olduğuna bir bakalım:</p>
<table style="border-collapse:collapse;border:none" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td style="border:solid #3366FF 1.0pt;background:#99CCFF; padding:0in 5.4pt 0in 5.4pt" valign="top"><strong><span style="font-size:9.0pt;font-family:">Operator</span></strong></td>
<td style="border:solid #3366FF 1.0pt;border-left:none;background:#99CCFF;padding:0in 5.4pt 0in 5.4pt" valign="top"><strong><span style="font-size:9.0pt;font-family:">Category</span></strong></td>
<td style="border:solid #3366FF 1.0pt;border-left:none;background:#99CCFF;padding:0in 5.4pt 0in 5.4pt" valign="top"><strong><span style="font-size:9.0pt;font-family:">Declaration Signature</span></strong></td>
<td style="border:solid #3366FF 1.0pt;border-left:none;background:#99CCFF;padding:0in 5.4pt 0in 5.4pt" valign="top"><strong><span style="font-size:9.0pt;font-family:">Symbol Mapping </span></strong></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Implicit</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Conversion</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Implicit(a : type) : resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">implicit typecast</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Explicit</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Conversion</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Explicit(a: type) : resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">explicit typecast</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Negative</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Unary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Negative(a: type) : resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">-</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Positive</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Unary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Positive(a: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">+</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Inc</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Unary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Inc(a: type) : resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Inc</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Dec</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Unary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Dec(a: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Dec</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">LogicalNot</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Unary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">LogicalNot(a: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">not</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">BitwiseNot</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Unary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">BitwiseNot(a: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">not</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Trunc</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Unary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Trunc(a: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Trunc</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Round</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Unary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Round(a: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Round</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Equal</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Comparison</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Equal(a: type; b: type) : Boolean;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">=</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">NotEqual</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Comparison</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">NotEqual(a: type; b: type): Boolean;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:"><></span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">GreaterThan</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Comparison</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">GreaterThan(a: type; b: type) Boolean;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">></span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">GreaterThanOrEqual</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Comparison</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">GreaterThanOrEqual(a: type; b: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">>=</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">LessThan</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Comparison</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">LessThan(a: type; b: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:"><</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">LessThanOrEqual</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Comparison</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">LessThanOrEqual(a: type; b: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:"><=</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Add</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Binary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Add(a: type; b: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">+</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Subtract</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Binary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Subtract(a: type; b: type) : resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">-</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Multiply</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Binary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Multiply(a: type; b: type) : resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">*</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Divide</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Binary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Divide(a: type; b: type) : resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">/</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">IntDivide</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Binary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">IntDivide(a: type; b: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">div</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Modulus</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Binary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Modulus(a: type; b: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">mod</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">LeftShift</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Binary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">LeftShift(a: type; b: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">shl</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">RightShift</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Binary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">RightShift(a: type; b: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">shr</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">LogicalAnd</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Binary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">LogicalAnd(a: type; b: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">and</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">LogicalOr</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Binary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">LogicalOr(a: type; b: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">or</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">LogicalXor</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Binary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">LogicalXor(a: type; b: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">xor</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">BitwiseAnd</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Binary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">BitwiseAnd(a: type; b: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">and</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">BitwiseOr</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Binary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">BitwiseOr(a: type; b: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">or</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">BitwiseXor</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">Binary</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">BitwiseXor(a: type; b: type): resultType;</span></td>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">xor</span></td>
</tr>
</tbody>
</table>
<p> Gördüğünüz gibi pek çok tanımlı operatörümüz var. Bu operatörlerimizin tek tek ne olduğunu ifade etmeden önce neden yeni bir string tipi tanımlamak istediğimizi ve bize getirilerinin neler olabileceği hakkında tüm söylediklerimize ek olarak bir kaç kelam daha etmekte fayda olabilir.</p>
<p> .Net dillerinde; bir string değişken tanımlandığında o string türü ile ilgili işlem yapabilecek metodların hepsine nokta(.) ya basarak erişebilme gücüne sahip olursunuz. Böylece bir veri türü üzerinde ne gibi işlemler yapılabileceğini bir bakışta gözlemleyebilirsiniz. Aynı zamanda; o veri türü ile ilgili işlem yapan metodların isimlerini ezberlemek yada hangi unit&#8217;te tanımlandıklarını hatırlamak ve uses bloğunda referans vermek durumunda kalmazsınız. Ayrıca; operator overloading herhangi bir veri türü ile yapılması derleyici tarafından yasaklanmış işlemleri kodlamanıza da imkan sağlayacaktır(Bir string&#8217;in tersini almak için başına eksi koymak gibi).</p>
<p> Bu faydalar; operator overloading kavramını programlarımızda bir yardımcı unsur olarak kullanmamız için yeterli olabilecek sebeplerdendir. Tüm bu teorik verilerin ışığında artık kodlamaya geçmeye başlayabiliriz. Tahmin edebileceğiniz gibi, yeni veri türümüz string veri türü ile iletişim kurabilen, ek özellikler getirebilen bir <span style="color: #ff9900;">record</span> olacaktır.</p>
<pre class="brush: delphi">
type
  NewString = record
  end;

..
..
var
  Old : String;
  New : NewString;
begin
  Old := &#039;Merhaba&#039;;
  New := Old;
end;
</pre>
<p> Örneğimizde <span style="color: #ff9900;">NewString</span> isminde bir record tanımladık ve içinde herhangi bir metod tanımlamadık. Ardından Newstring türündeki değişkenimize string türündeki değişkenimizi atamaya çalıştık. Ancak; derleyici tam bu satırda bize, <span style="color: #ff9900;">&#8220;Incompatible types: &#8216;NewString&#8217; and &#8217;string&#8217;&#8221;</span> hatası verecektir. Her iki tipin birbirine nasıl atanacağı bu noktada derleyici tarafından bilinmemektedir. Bu noktada yapacağımız şey; derleyiciye yeni tipimizin string veri türü ile atama sırasında ne gibi bir işlem yapması gerektiği hususunda bilgi vermektir. Kullanmamız gereken <span style="color: #ff9900;">Implicit</span> adındaki operatör metodudur. Implicit operatörü yeni tanımladığımız record&#8217;umuza atama yapılacağı sırada ne gibi bir kod çalıştırmamız gerektiğini söyleyecektir. Yeni kodumuz aşağıdaki gibi olacaktır.</p>
<pre class="brush: delphi">
type
  NewString = record
  private
    fData : String;
  public
    class operator Implicit(const Left : String) : NewString;
  end;

implementatiton

class operator NewString.Implicit(const Left : String) : NewString;
begin
  Result.fData := Left;
end;
..
..
var
  Old : String;
  New : NewString;
begin
  Old := &#039;Merhaba&#039;;
  New := Old;
end;
</pre>
<p> Gördüğünüz gibi NewString record&#8217;umuz içinde private bölümünde fData isminde ve string türünde bir değişken tanımladık. Ardından; NewString veri türümüze atama yapmak istediğimiz veri türü string olduğu için Implicit operatörünü string verileri alabilecek şekilde tanımladık. Bu aşamada; New := Old; ataması Implicit kodumuza dallanılmasına sebep olacaktır. Implicit operatörü bilinçsiz tür dönüşüm operatörü olarak da bilinir. New adlı ve NewString türlü değişkenimize string türündeki verilerin atanmaya çalışılması her zaman Implicit metodumuza başvurulmasına neden olacaktır. Ancak; New := 123; gibi bir atama yine derleyici tarafından bir hata ile neticelenecektir. Çünkü Integer veri türü ile atama uyumluluğuna sahip değiliz. Integer veri türü ile ilgili bir atamanında geçerli olması isteniyorsa; Integer veri türü içinde bir Implicit metodunun yazılması icap edecektir.</p>
<p> <span style="color: #ff9900;">Explicit</span> ise; bilinçli tür dönüşüm operatörüdür. Bazen veri türlerimizi başka bir veri türüne dönüştürmek için casting yaparız. Bu tarz durumlarda explicit operatöründe yazdığımız kodlar devreye girecektir. Örneğin; Old := String(New); türündeki bir kullanım explicit operatör çağrımına neden olacaktır. O halde explicit operatörümüzü de ekleyerek ilerlemeye devam edelim:</p>
<pre class="brush: delphi">
type
  NewString = record
  private
    fData : String;
  public
    class operator Implicit(const Left : String) : NewString;
    class operator Explicit(const Left : NewString) : String;
  end;

implementatiton

class operator NewString.Implicit(const Left : String) : NewString;
begin
  Result.fData := Left;
end;

class operator NewString.Explicit(const Left : NewString) : String;
begin
  Result := Left.fData;
end;

..
..
var
  Old : String;
  New : NewString;
begin
  Old := &#039;Merhaba&#039;;
  New := Old;

  Old := String(New);
end;
</pre>
<p> String ifadeler sözkonusu olduğunda sık kullandığımız operatörlerden birisi de +(Artı) operatörüdür. Bunun için Delphi&#8217;de kullanmamız gereken operatörün <span style="color: #ff9900;">Add</span> operatörü olduğunu tahmin edersiniz. Ancak; ilgili tablodan gördüğünüz gibi Add operatörünün kullanılması , Implicit ve Explicit&#8217;in kullanımlarından biraz daha farklıdır. Add metodu içine toplanması istenen iki veri türünü de parametre olarak beklemektedir. Biz bu durumda tüm olasılıklara uygun Add metodlarını yazmak durumunda kalırız. Arzu ederseniz onu da kodlayalım;</p>
<pre class="brush: delphi">
  NewString = record
  private
    fData : String;
  public
    class operator Implicit(const Left : String) : NewString;
    class operator Explicit(const Left : NewString) : String;

    class operator Add(const Left : String; const Right : NewString) : NewString; // 1.nci metod: New := &#039;Test&#039; + New; gibi bir kullanım.
    class operator Add(const Left : NewString; const Right : String) : NewString; // 2.nci metod: New := New + &#039;Test&#039;; gibi bir kullanım.
    class operator Add(const Left : NewString; const Right : NewString) : NewString; // 3.ncü metod: New := New + New; gibi bir kullanım.
  end;

implementatiton

class operator NewString.Implicit(const Left : String) : NewString;
begin
  Result.fData := Left;
end;

class operator NewString.Explicit(const Left : NewString) : String;
begin
  Result := Left.fData;
end;

class operator NewString.Add(const Left : String; const Right : NewString) : NewString;
begin
  Result.fData := Left + Right.fData;
end;

class operator NewString.Add(const Left : NewString; const Right : String) : NewString;
begin
  Result.fData := Left.fData + Right;
end;

class operator NewString.Add(const Left : NewString; const Right : NewString) : NewString;
begin
  Result.fData := Left.fData + Right.fData;
end;

..
..
var
  Old : String;
  New : NewString;
begin
  Old := &#039;Merhaba&#039;;
  New := Old;

  Old := String(New);

  New := &#039;Dünya &#039; + New; // &quot;Dünya Merhaba&quot; sonucunu elde edeceğiz. 1.nci metod çağrılacaktır.
  New := New + &#039; Dünya&#039;; // &quot;Merhaba Dünya&quot; sonucunu elde edeceğiz. 2.nci metod çağrılacaktır.
  New := New + New;      // &quot;MerhabaMerhaba&quot; sonucunu elde edeceğiz. 3.ncü metod çağrılacaktır.
end;
</pre>
<p> Add operatöründe yada onun gibi birden fazla veri türü ile işlem yapan diğer operatör metodlarında yegane kural; geçilmesi gereken parametrelerden en az bir tanesinin NewString türünde olması gerektiğidir. Kısacası aşağıdaki gibi bir tanıma sahip olamayız:</p>
<pre class="brush: delphi">
type
  NewString = record
  ..
  ..
    class operator Add(const Left : String; const Right : String); // Her iki parametre türü de string olduğu için kabul edilmez.!
  end;
</pre>
<p> String veri türlerinde çıkartma işlemine pek sık rastlanmadığı için <span style="color: #ff9900;">Subtract</span> operatörü ile ilgilenmeyeceğiz ancak onun da Add operatöründen farklı olmadığını bilmenizi isterim. Başlarda verdiğimiz örneklerde bir string ifadeyi belli bir sayı ile çarparak o string&#8217;den birden fazla elde etmek ve string ifadenin başına eksi(-) operatörünü koyarak ilgili string ifadeyi ters çevirmeyi denemiş ve hata ile neticeleneceğini ifade etmiştik. Şimdi gelin, bu operatörleri de yeni veri türümüz olan NewString için kodlayalım. Kullanacağımız operatörler tahmin edeceğiniz gibi, <span style="color: #ff9900;">Multiply ve Negative</span> operatörleri olacak;</p>
<pre class="brush: delphi">
class operator NewString.Multiply(const Left : NewString; const Right : Integer) : NewString; // 1.nci metod
begin
  Result.fData := DupeString(Left.fData, Right);
end;

class operator NewString.Multiply(const Left : Integer; const Right : NewString) : NewString; // 2.nci metod
begin
  Result.fData := DupeString(Right.fData, Left);
end;

class operator NewString.Negative(const Left : NewString) : NewString;
begin
  Result.fData := ReverseString(Left.fData);
end;

..
..
var
  New : NewString;
begin
  New := &#039;Merhaba&#039;; // Implicit çağrımına neden olur.
  New := New * 3; // &quot;MerhabaMerhabaMerhaba&quot; sonucunu döndürür. 1.nci metod çağrılacaktır.

  New := &#039;Dünya&#039;; // Implicit çağrımına neden olur.
  New := 3 * New; // &quot;DünyaDünyaDünya&quot; sonucunu döndürür. 2.nci metod çağrılacaktır.

  New := &#039;Merhaba Dünya&#039;; // Implicit çağrımına neden olur.
  New := -New; // Negative çağrımına neden olur. &quot;aynüD abahreM&quot; sonucunu döndürecektir.
end;
</pre>
<p> Bunların ardından olmazsa olmazlarımızdan <span style="color: #ff9900;">Equal ve NotEqual</span> operatör metodlarını kodlamamız gerekiyor. Bu metodların kodlanmaması NewString veri türümüz için karşılaştırma işlemleri yapamamamız anlamını taşıyacaktır. </p>
<pre class="brush: delphi">
class operator NewString.Equal(const Left: String; // 1.nci metod
  const Right: NewString): Boolean;
begin
  Result := Left = Right.fData;
end;

class operator NewString.Equal(const Left: NewString; const Right: String): Boolean; // 2.nci metod
begin
  Result := Left.fData = Right;
end;

class operator NewString.Equal(const Left, Right: NewString): Boolean; // 3.ncü metod
begin
  Result := Left.fData = Right.fData;
end;

class operator NewString.NotEqual(const Left, Right: NewString): Boolean; // 1.nci metod
begin
  Result := Left.fData &lt;&gt; Right.fData;
end;

class operator NewString.NotEqual(const Left: NewString; // 2.nci metod
  const Right: String): Boolean;
begin
  Result := Left.fData &lt;&gt; Right;
end;

class operator NewString.NotEqual(const Left: String; // 3.ncü metod
  const Right: NewString): Boolean;
begin
  Result := Left &lt;&gt; Right.fData;
end;
..
..
var
  New, New2 : NewString;
begin
  New := &#039;Merhaba&#039;;
  New2:= &#039;Merhaba&#039;;

  if &#039;Merhaba&#039; = New then ShowMessage(&#039;Merhaba = New&#039;); // 1.nci metod çağrılacaktır.
  if New = &#039;Merhaba&#039; then ShowMessage(&#039;New = Merhaba&#039;); // 2.nci metod çağrılacaktır.
  if New = New2 then ShowMessage(&#039;New = New2&#039;); // 3.ncü metod çağrılacaktır.

  if New &lt;&gt; New2 then ShowMessage(&#039;New &lt;&gt; New2&#039;); // 1.nci metod çağrılacaktır.
  if New &lt;&gt; &#039;Merhaba&#039; then ShowMessage(&#039;New &lt;&gt; Merhaba&#039;); // 2.nci metod çağrılacaktır.
  if &#039;Merhaba&#039; &lt;&gt; New then ShowMessage(&#039;Merhaba &lt;&gt; New&#039;); // 3.ncü metod çağrılacaktır.
end;
</pre>
<p>Diğer operatörlerimiz belki string veri türümüz için anlamlı olmayacaktır. Ancak, sizler kendi yazacağınız veri türlerinde ilgili operatör metodlarını da kullanabilirsiniz. Ben; sıklıkla eksikliğini hissettiğimiz String ve TDatetime veri türleri ile ilgili yeni tip tanımına gitmeyi faydalı buldum. Yazmış olduğum yeni tipleri indirebilmeniz için makalenin sonunda bir link vereceğim.</p>
<p> Makalemizi neticelendirmeden evvel yeni tiplerimiz ile neler yapabileceğimize dair küçük bir kaç örnek vermek faydalı olacaktır:</p>
<pre class="brush: delphi">
procedure Log(const Message : String);
begin
  form1.Memo1.Lines.Add(Message);
end;

var
  New : NewString;
  arr   : TList&lt;Char&gt;;
  arr2 : TList&lt;String&gt;;
  ch   : Char;
  str : String;

  Len,
  Index : Integer;
begin
  New := &#039;Merhaba Dünya&#039;;
  Log(New.ToString);

  arr := New.ToChararray;
  for ch in arr do Log(ch);

  arr := New.ToCharArray(5, 6);
  for ch in arr do Log(ch);

  if New.Contains(&#039;Merhaba&#039;) then Log(&#039;Merhaba bulundu&#039;);
  if New.EndsWith(&#039;Dünya&#039;) then Log(&#039;Dünya stringi ile bitiyor&#039;);
  if New.StartsWith(&#039;Merhaba&#039;) then Log(&#039;Merhaba stringi ile başlıyor&#039;);
  Index := New.IndexOf(&#039;Dünya&#039;);
  Len := New.Length;
  New := New.Trim;
  New := New.Replace(&#039;a&#039;, &#039;x&#039;);
  New := New.SubString(5, 5);
  New := New.ToUpper;
  New := New.ToLower;

  New := &#039;Merhaba,Dünya&#039;;
  arr2 := New.Split(&#039;,&#039;);
  for str in arr2 do Log(str); // Merhaba ve Dünya
end;

var
  NewDT : NewDateTime;
  dt : TDateTime;
begin
  NewDT := Now;
  NewDT := Date;
  NewDT := &#039;bugün&#039;;
  NewDT := &#039;yarın&#039;;
  NewDT := &#039;+5y&#039;; // Bugünün tarihinden 5 yıl sonrası.
  NewDT := Now + &#039;-3y +5a -7g +6s +15dk +30sn&#039;; // Bugünden 3 yıl 7 gün öncesinden 5 ay 6 saat 15 dakika 30 saniye sonrası.
  NewDT := Date + 5; // 5 gün sonrası.
  Log(NewDT.ToString);
  Log(InttoStr(NewDT.Day));
  Log(InttoStr(NewDT.Month));
  Log(InttoStr(NewDT.Year));
  Log(InttoStr(NewDT.Hour));
  Log(InttoStr(NewDT.Minute));
  Log(InttoStr(NewDT.Second));
  Log(InttoStr(NewDT.MilliSecond));

  NewDT.Year := NewDT.Year + 3;
  dt := NewDT.ToDateTime;
  Log(DateToStr(dt));

  Inc(NewDT);
  Dec(NewDT);

  NewDT := NewDT - 7;
  NewDT := NewDT - &#039;3g 5s&#039;;
end;
</pre>
<p><a href="http://www.tugrulhelvaci.com/wp-content/uploads/DateTimeUtils.pas">NewDateTime</a> ve <a href="http://www.tugrulhelvaci.com/wp-content/uploads/StringUtils.pas">Newstring</a> isimli dosyaları da buradan indirebilirsiniz.</p>
<p>Sevgiler, saygılar..</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tugrulhelvaci.com/?feed=rss2&amp;p=599</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Interface Nedir, Nerelerde ve Neden Kullanırız ?</title>
		<link>http://www.tugrulhelvaci.com/?p=582</link>
		<comments>http://www.tugrulhelvaci.com/?p=582#comments</comments>
		<pubDate>Sun, 30 May 2010 03:36:39 +0000</pubDate>
		<dc:creator>Tuğrul HELVACI</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Abstract Classes]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[QueryInterface]]></category>
		<category><![CDATA[TInterfacedObject]]></category>
		<category><![CDATA[_AddRef]]></category>
		<category><![CDATA[_Release]]></category>

		<guid isPermaLink="false">http://www.tugrulhelvaci.com/?p=582</guid>
		<description><![CDATA[  Şu son bir kaç gündür, Delphi 2010 ve getirdiği yenilikler ile ilgileniyorum. Bu bağlamda; Generic Liste sınıfları ve bu liste sınıflarının özelleştirilmesi ve çeşitli yeteneklerle zenginleştirilmesi üzerine geliştirmeler yapıyorum. Sizlerle bu yaptıklarımı paylaşabilmem için öncelikle bazı temel hususları izah etmek gerektiğini düşündüm. Bu bağlamda; ilk olarak Interface&#8217;ler hakkında bir makale, ardından operator overloading [...]]]></description>
			<content:encoded><![CDATA[<p>  Şu son bir kaç gündür, Delphi 2010 ve getirdiği yenilikler ile ilgileniyorum. Bu bağlamda; Generic Liste sınıfları ve bu liste sınıflarının özelleştirilmesi ve çeşitli yeteneklerle zenginleştirilmesi üzerine geliştirmeler yapıyorum. Sizlerle bu yaptıklarımı paylaşabilmem için öncelikle bazı temel hususları izah etmek gerektiğini düşündüm. Bu bağlamda; ilk olarak Interface&#8217;ler hakkında bir makale, ardından operator overloading konusu ve nihayet generic&#8217;ler ve kendi kendini yoketme becerisine sahip bazı özel mekanizmaları işleyeceğiz. Aslında tüm bu hususlara girmeden, özetle nelerin yapılabileceğini paylaşıp örneğimi verebilir ve geçebilirdim. Ancak bu yaklaşım tarzı; benim programlamaya bakış açıma aykırı olduğu ve sizlere pek bir şey kazandıracağına inanmadığım için kendi adıma zor olanı seçip, mümkün mertebe en derinlere kadar inmeye gayret edeceğim. </p>
<p>Hedefimize ilerlerken ilk durağımız olan Interface&#8217;lerde biraz durup soluklanalım ve bu kavramın ne olduğu yada ne olmadığı; neleri başarıp neleri başaramayacağı; neden var olduğu ve nerelerde kullanabileceğimiz hakkında nacizane bilgilerimiz ile konunun karanlık köşelerine ışık tutmaya gayret edelim.</p>
<p> Interface kelimesi dilimizde genellikle <strong>&#8220;Arayüz&#8221;</strong> yada <strong>&#8220;Arabirim&#8221;</strong> olarak kullanılmaktadır. Pek çok programcımız için Interface&#8217;ler ya çok basit bir yapı olarak  ya da COM programlamanın fıtratı gereği çok karmaşık bir yapı olarak algılanır. Aslında her ikisi de doğrudur. Yapısal görünümü itibari ile son derece basit görünen Interface&#8217;ler aslında yapabildikleri ve nasıl yaptıkları itibarı ile de son derece kompleks yapılardır. Bir interface görünüm ve yapı itibari ile bir sınıfa çok benzer. Ancak elbette bu makaleye konu olan pek çok farklılıkları da mevcuttur. Arzu ederseniz makalemizde ilerlemeden önce çok basit bir interface tanımının Delphi&#8217;deki yapısını görelim ve ardından ilerlemeye devam edelim:</p>
<pre class="brush: delphi">
  IFlyable = interface
  end;
</pre>
<p> Gördüğünüz gibi bir sınıf tanımına son derece benziyor. Ancak bazı farklılıkları var. IFlyable(uçabilen) arabiriminin tanımlamasında &#8220;class&#8221; ayrılmış sözcüğü yerine <strong>&#8220;interface&#8221;</strong> sözcüğünün geçtiğini gözlemliyoruz. Şu anda gözlemleyebildiğimiz başka bir fark bulunmuyor. Aslında interface&#8217;ler özet sınıf adını verdiğimiz <span style="color: #ff9900;">abstract</span> sınıflara çok benzerler. Abstract sınıflar, kendisini miras alacak sınıflar için ileride kullanılmasının zorunlu olduğu metodların birer özetlerini içerirler. Ancak bu sınıflar aynı zamanda gerçek kod bloklarına da ev sahipliği yapabilirler. Interface&#8217;lerin ve abstract(özet) sınıfların ortak özellikleri; kendilerini kullanacak olan alt sınıflarda tanımlanması gereken metodların şablonlarına ev sahipliği yapmaktır.<br />
<span id="more-582"></span><br />
 Bir interface&#8217;te yada abstract sınıfta tanımlanmış olan bir metod; bu interface yada abstract sınıfı kullanmak isteyen alt sınıflarda kesinlikle tanımlanmalıdır(Gerçekte bir abstract sınıf içinde tanımlı olan bir metod, bu abstract sınıfı miras alan alt sınıfta tanımlanmayabilir, ancak alt sınıftaki metod çağrımı bir hataya neden olacağı için genellikle tanımlanmak zorundadır.). Aksi durumda derleyici kodunuzu derlemenize izin vermez. Şu aşamaya kadar söylediklerimizi küçük bir örnek ile göstermeye çalışalım;</p>
<pre class="brush: delphi">
  IFlyable = interface
    procedure Fly;
  end;

  TPlaneInterface = class(TInterfacedObject, IFlyable)
    procedure Fly;
  end;

  TFlyable = class abstract
    procedure Fly; virtual; abstract;
  end;

  TPlaneClass = class(TFlyable)
    procedure Fly; override;
  end;
</pre>
<p> Yukarıdaki kısa kod örneğimizde bir interface ve o interface&#8217;i implemente(uygulayan) eden bir sınıf; bir abstract sınıf ve o sınıfın bir mirasçısını görüyorsunuz. Tanımlarda bazı farklılıklar var. IFlyable interface&#8217;i içindeki Fly metodu için editörümüzde herhangi bir kod yazmanıza gerek yoktur. Ancak bu interface&#8217;i implemente edecek olan sınıfta (TPlaneInterface) Fly metodunun yazılması zorunludur. Bu tanıma çok benzer bir şekilde TFlyable isimli abstract sınıfımızında kod editöründe herhangi bir kodu yoktur ve interface&#8217;lerde olduğu gibi abstract sınıfı miras alan (TPlaneClass) sınıflarda Fly metodunun yeniden tanımlanması icap eder. Eğer TPlaneClass sınıfı içinde Fly metodunu yeniden tanımlamazsanız derleyici kodunuzu düzgün bir şekilde derleyebilecektir ancak çalışma anında Fly metodunu çağırırsanız bir <span style="color: #ff9900;">&#8220;Abstract Error&#8221;</span> hatası alırsınız. Bu hataların oluşmasına mani olmak için, abstract sınıflarda genellikle <span style="color: #ff9900;">&#8220;virtual; abstract&#8221;</span> olarak tanımlanan metodların alt sınıflarda yeniden tanımlanması gerekir.</p>
<p> Bu bağlamda interface&#8217;ler ve abstract sınıflar birbirlerine son derece benzerler. Genellikle programcılarımız abstract sınıfların varlığı karşısında interface&#8217;lerin karmaşık ve gereksiz mekanizmalar olduğunu düşünürler. Abstract sınıflar interfacelerden farklı olarak içlerinde gerçek kod parçacıkları da bulundurabilirler. Bu yetenekleri ile interface&#8217;lerden daha geniş bir kullanıma sahip oldukları ve aynı zamanda daha nesnesel ve kolay oldukları düşünülür. Bu elbette bir bakış açısıdır, ancak tamamiyle doğru değildir.</p>
<p> Şimdi bir abstract sınıfın içinde gerçek kod parçalarının da olabileceğine bir örnek verelim;</p>
<pre class="brush: delphi">
  TFlyable = class abstract
   private
    fField : Integer;

    procedure Fly; virtual; abstract;
   public
    procedure RealCode; virtual;
  end;
 //...
 //...
  procedure TFlyable.RealCode;
  begin
    //..
    //..
  end;
</pre>
<p> Görüldüğü üzere, bir abstract sınıfın içinde hem şablon bir metod hemde gerçek bir kod yazabildik. Aynı zamanda public, private gibi erişim belirleyicileri kullanabildik ve ihtiyacımız olan değişkenleri de tanımlayabildik.</p>
<p> Evet bu noktaya kadar bir interface&#8217;in yada bir abstract sınıfın neye benzediği yönünde küçük bazı örnekler gördük. Ancak interface&#8217;in neden var olduğu, abstract ve interface&#8217;lerin birbirlerine alternatifmi oldukları hakkında henüz herhangi bir yorum yapmadık.</p>
<p> Interface&#8217;ler; içlerinde istediğiniz kadar metod tanımlayabileceğiniz ve bu metodlar için kod yazmanıza gerek olmayan sanal yapılardır. Bu interface&#8217;i implemente edecek olan sınıflarınızın yeteneklerinin ne olduğunu özetleyebilen özet yapılar olarak düşünülebilir. Interface&#8217;ler içlerinde metod tanımlarını barındırabilirler ancak, değişken tanımına yada erişim kısıtlayıcılara sahip olamazlar. Yani; bir interface&#8217;in içinde herhangi bir türden değişken tanımlayamazsınız , tanımlayabildiğiniz tüm metodlar; public olarak kabul görür ve interface&#8217;in içinde public, private, protected kullanamazsınız. Bu özellikleri ile abstract sınıflardan ayrılırlar.</p>
<p>Aynı zamanda Interface&#8217;ler bir sınıftan yada bir record&#8217;dan türeyemezler. Bir Interface; sadece bir başka interface&#8217;den türeyebilir. Benzer bir şekilde herhangi bir sınıfta direkt olarak bir interface&#8217;den türeyemez.</p>
<p>Geçersiz kullanımlara kısaca bir göz atalım arzu ederseniz;</p>
<pre class="brush: delphi">
  ITest = interface
  end;

  IFlyable = interface(TObject) // Geçersiz
  end;

  IFlyable = interface(ITest) // Geçerli
  end;

  TFlyable = class abstract(IFlyable) // Geçersiz
  end;

  TFlyable = class abstract// Geçerli
  end;

  TFlyable = class abstract(TComponent) // Geçerli
  end;

  TFlyable = class abstract(TInterfacedObject, IFlyable) // Geçerli
  end;
</pre>
<p> Görüldüğü gibi, bir interface bir sınıftan miras alınarak tanımlanamaz. Ancak ve ancak bir başka interface&#8217;i miras alabilir. Oysaki sınıf tanımlarında abstract olsun yada olmasın durum biraz daha farklıdır. Bir sınıf bir interface&#8217;i direkt olarak miras alamaz ancak bir yardımcı sınıf vasıtası ile(<span style="color: #ff9900;">TInterfacedObject</span>) interface&#8217;leri de miras alabilir.</p>
<p>Tüm bu anlatılanlar ışığında kısa bir özet vermek sanırım iyi olacaktır.</p>
<ul>
<li>Interface&#8217;ler şablon tanımlarıdır, ve gerçek kod parçacıkları içermezler.</li>
<li>Interface&#8217;ler içlerinde değişken tanımlarına ve erişim kısıtlayıcılarına(public, private vb.) sahip olamazlar.</li>
<li>Interface&#8217;ler sınıflardan miras alınamazlar, sadece bir başka interface&#8217;den miras alınabilirler.</li>
<li>Interface&#8217;leri implemente eden sınıflarda interface&#8217;in sahip olduğu tüm metodların uygulanması gerekir.</li>
</ul>
<ul>
<li>Abstract sınıflar şablon tanımlarıdır ama gerçek kod parçacıkları da içerebilirler.</li>
<li>Abstract sınıflar içlerinde değişken tanımlarına ve erişim kısıtlayıcılarına(public, private vb.) sahip olabilirler.</li>
<li>Abstract sınıflar interfacelerden direkt olarak miras alınamazlar, Interface&#8217;leri implemente edebilmeleri için TInterfacedObject gibi bir sınıfı kullanmaları gerekir.</li>
<li>Abstract sınıfları  implemente eden sınıflarda abstract sınıfın içinde tanımlı olan tüm virtual; abstract; metodların implemente edilmesi önerilir ancak bunun aksi bir durum derleyici tarafından da kabul görür. Bu gibi bir durumda derleyici abstract sınıfı miras alan sınıfı create etmeye çalıştığınız kod bloğu için bir warning mesajı verecektir.</li>
</ul>
<p> Sanıyorum artık, abstract sınıflar ve interface&#8217;lerle ilgili az da olsa bir fikir sahibiyiz. Bu iki birbirine yakın kullanım temelde aynı amaca hizmet etmektedir. Ancak birbirinden farklı bir sözdizimine sahiptir. Peki, biz hangi durumlarda hangisini kullanacağız ?</p>
<p> Bu sorunun yanıtı abstract sınıf ve interface ayrımına gidilmesinin nedenlerini izah için yeterli olacaktır. Hepimizin bildiği gibi Delphi, C# gibi diller <span style="color: #ff9900;">multiple inheritance</span> adı verilen çoklu kalıtımı desteklemezler. Ancak C++ gibi diller çoklu kalıtıma imkan sağlarlar. Bir sınıfın birden fazla atasının olması gibi bir konseptin Delphi, C# gibi dillerde olmaması belki karmaşanın önüne geçmek içindir. Ancak çoklu kalıtıma ihtiyaç duyduğumuz yerlerde olabilir. Kısa bir örnek vererek daha da anlamlandırmaya çalışalım.</p>
<p> Diyelim ki, taşıtlarla ilgili bir sınıf hiyerarşisi oluşturmak istiyoruz. Bu hiyerarşiyi hem interface ile hem de abstract sınıflar ile yapmaya çalışalım:</p>
<pre class="brush: delphi">
// Interfacelerle ilgili tanımlamalar...

  ITasit = interface
    procedure MotoruCalistir;
    procedure MotoruDurdur;
  end;

  IUcabilenTasitlar = interface(ITasit)
    procedure Uc;
  end;

  TUcabilenTasitlar = class(TInterfacedObject, IUcabilenTasitlar)
  public
    procedure MotoruCalistir;
    procedure MotoruDurdur;

    procedure Uc;
  end;

  IYuzebilenTasitlar = interface(ITasit)
    procedure Yuz;
  end;

  TYuzebilenTasitlar = class(TInterfacedObject, IYuzebilenTasitlar)
  public
    procedure MotoruCalistir;
    procedure MotoruDurdur;

    procedure Yuz;
  end;

  IKaradaGidebilenTasitlar = interface(ITasit)
    procedure Yuru;
  end;

  TKaradaGidebilenTasitlar = class(TInterfacedObject, IKaradaGidebilenTasitlar)
  public
    procedure MotoruCalistir;
    procedure MotoruDurdur;

    procedure Yuru;
  end;

// Abstract sınıflarla ilgili tanımlamalar...

  TTasitAbstract = class abstract
  public
    procedure MotoruCalistir; virtual; abstract;
    procedure MotoruDurdur; virtual; abstract;
  end;

  TUcabilenTasitlarAbstract = class abstract(TTasitAbstract)
  public
    procedure Uc; virtual; abstract;
  end;

  TUcabilenTasitlar = class(TUcabilenTasitlarAbstract)
  public
    procedure MotoruCalistir; override;
    procedure MotoruDurdur; override;

    procedure Uc; override;
  end;

  TYuzebilenTasitlarAbstract = class abstract(TTasitAbstract)
  public
    procedure Yuz; virtual; abstract;
  end;

  TYuzebilenTasitlar = class(TYuzebilenTasitlarAbstract)
  public
    procedure MotoruCalistir; override;
    procedure MotoruDurdur; override;

    procedure Yuz; override;
  end;

  TKaradaGidebilenTasitlarAbstract = class abstract(TTasitAbstract)
  public
    procedure Yuru; virtual; abstract;
  end;

  TKaradaGidebilenTasitlar = class(TKaradaGidebilenTasitlarAbstract)
  public
    procedure MotoruCalistir; override;
    procedure MotoruDurdur; override;

    procedure Yuru; override;
  end;

// Interface sınıflarından yada abstract sınıflardan türeyebilen sınıflar

  TUcak = class(TUcabilenTasitlar)
  end;

  TGemi = class(TYuzebilenTasitlar)
  end;

  TAraba = class(TKaradaGidebilenTasitlar)
  end;
..
..
..
var
  BMW, Audi, Mercedes  : TAraba;
  Gemi, Yat, Tekne  : TGemi;
  Piper, Jet, Zeplin  : TUcak;
begin
  BMW := TAraba.Create;
  BMW.Yuru;

  Audi := TAraba.Create;
  Audi.Yuru;

  Mercedes := TAraba.Create;
  Mercedes.Yuru;

  Gemi := TGemi.Create;
  Gemi.Yuz;

  Yat := TGemi.Create;
  Yat.Yuz;

  Tekne := TGemi.Create;
  Tekne.Yuz;

  Piper := TUcak.Create;
  Piper.Uc;

  Jet := TUcak.Create;
  Jet.Uc;

  Zeplin := TUcak.Create;
  Zeplin.Uc;

  BMW.Free;
  Audi.Free;
  Mercedes.Free;

  Gemi.Free;
  Yat.Free;
  Tekne.Free;

  Piper.Free;
  Jet.Free;
  Zeplin.Free;
end;
</pre>
<p> Yukarıdaki sınıf tanımlarımızda uçabilen, yüzebilen ve karada gidebilen taşıtları hem interface&#8217;ler vasıtası ile hem de abstract sınıflar vasıtası ile tanımladık. Ardından TAraba, TGemi ve TUcak sınıflarını bu sınıflardan türettik ve ilgili nesnelerimizi oluşturabildik. Bu aşamaya kadar herhangi bir dizayn problemi ile karşılaşmadık. <strong>Ancak; ya hem karada hem denizde gidebilen howercraft türü bir taşıtımız varsa ne yapacağız ? Bu aracı hangi sınıftan türeteceğiz ?</strong> Abstract yaklaşımla yapmaya çalıştığımız da aşağıdaki gibi bir kullanıma ihtiyacımız olacak;</p>
<pre class="brush: delphi">
  THemKaradaHemDenizdeGidebilenTasitlarAbstract = class abstract(TYuzebilenTasitlarAbstract, TKaradaGidebilenTasitlarAbstract)
  end;
</pre>
<p> Ancak hepimizin bildiği gibi ne Delphi ne de C# gibi diller bu tarz bir tanımlamaya(Multiple Inheritance) müsaade etmiyorlar. Arzu ederseniz bir de Interface&#8217;ler ile yapmaya çalışalım;</p>
<pre class="brush: delphi">
  THemKaradaHemDenizdeGidebilenTasitlar = class(TInterfacedObject, IYuzebilenTasitlar, IKaradaGidebilenTasitlar)
   public
    procedure MotoruCalistir; // ITasit interface&#039;inden..
    procedure MotoruDurdur; // ITasit interface&#039;inden..

    procedure Yuz; // IYuzebilenTasitlar interface&#039;inden..
    procedure Yuru; // IKaradaGidebilenTasitlar interface&#039;inden..
 end;
..
..
..
var
  howercraft : THemKaradaHemDenizdeGidebilenTasitlar;
begin
  howercraft := THemKaradaHemDenizdeGidebilenTasitlar.Create;
  howercraft.Yuz;
  howercraft.Yuru;
  howercraft.Free;
end;
</pre>
<p>  Evet, görebildiğiniz gibi Delphi&#8217;de interface kullanarak multiple inheritance&#8217;ın yollarını açtık. Multiple Inheritance&#8217;a ihtiyaç duyulması interface kullanılması için gereken nedenlerden sadece birisidir. Diğer önemli neden, şu ana kadar hiç bahsetmediğimiz <span style="color: #ff9900;">Referans Sayma Mekanizmasıdır</span>. Makalemizin bu noktadan sonrası konunun başlığı gereği interface&#8217;lere yönelik olacaktır. Şu zamana kadar yazdıklarımız, genelde birbirine karıştırılan yada birbirinin arasındaki farkın çok net olmadığı iki özel teknolojinin zihnimizde daha da netleşebilmesi adına idi.</p>
<p> Referans sayımının detaylarına inmeden önce tüm interface&#8217;lerin <span style="color: #ff9900;">IInterface</span> arabiriminden türemiş olduğunu söylememiz gerekir. Nasıl ki yeni bir sınıf tanımlarken TMyClass = class tanımı ile TMyClass = class(TObject) tanımı aynı ise; interfacelerde de benzer bir şekilde IMyInterface = interface tanımı ile IMyInterface = interface(IInterface) tanımı aynıdır. Kısacası tüm interface&#8217;lerin atası IInterface arabirimidir. Şimdi gelin IInterface arabiriminin Delphi&#8217;deki tanımına yakından göz atalım:</p>
<pre class="brush: delphi">
  IInterface = interface
    [&#039;{00000000-0000-0000-C000-000000000046}&#039;]
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  end;
</pre>
<p> IInterface arabiriminin sahip olduğu 3 metodun da birbirinden önemli olduğunu ifade etmek isterim. Hatırlayacağınız üzere, interface tanımlarının bir sınıf tarafından direkt olarak miras alınmasının mümkün olmadığından bahsetmiştik. Bu sebeple bir interface&#8217;i implemente edebilmek adına örneklerimizde hep TInterfacedObject isimli sınıfı kullanmıştık. Peki nedir bu TInterfacedObject ? Bir de ona yakından bakalım;</p>
<pre class="brush: delphi">
  TInterfacedObject = class(TObject, IInterface)
  protected
    FRefCount: Integer;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  public
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
    class function NewInstance: TObject; override;
    property RefCount: Integer read FRefCount;
  end;
</pre>
<p> Gördüğünüz gibi TInterfacedObject sınıfı interface&#8217;lerin ata sınıfı olan IInterface arabirimini implemente etmiş durumda. Biz bundan sonraki örneklerimizde herhangi bir interface&#8217;i implemente ederken TInterfacedObject sınıfını kullanmaya devam edeceğiz. Ancak siz başka bir sınıftan miras almak istiyorsanız, IInterface&#8217;in içinde tanımlı olan 3 metodu da implemente etmeniz gerektiğini lütfen unutmayın.</p>
<p> Peki ne işe yarıyor bu 3 ayrı metod ? Bir kaç paragraf önce interface&#8217;lerin en önemli özelliklerinden bir tanesinin Referans Sayma Mekanizması olduğundan bahsetmiştik. İşte bu mekanizma bizim için son derece kullanışlı ve faydalı olacak. Delphi&#8217;de bütün interface&#8217;ler referans sayımlıdır. Kısaca referans sayımı; hafızadaki bir bellek bölgesine işaret eden herhangi bir nesneye kaç işaretçinin referansı olduğunu tutar. Hemen kısa bir örnek vererek izah etmeye gayret edelim:</p>
<pre class="brush: delphi">
type
  ITest = interface
  end;

  TTest = class(TInterfacedObject, ITest)
  public
    destructor Destroy; override;
  end;

var
  Test : ITest;

implementation

destructor TTest.Destroy;
begin
  ShowMessage(&#039;Nesne yok edilecek.!&#039;);
  inherited Destroy;
end;
..
..
var
   I1, I2, I3 : ITest;
   iRefCount : Integer;
begin
  Test := TTest.Create;
  iRefCount := TTest(Test).RefCount; // Referans = 1

  I1 := Test;
  iRefCount := TTest(Test).RefCount; // // Referans = 2

  I2 := Test;
  iRefCount := TTest(Test).RefCount; // Referans = 3

  I3 := Test;
  iRefCount := TTest(Test).RefCount; // Referans = 4

  I1 := nil;
  iRefCount := TTest(Test).RefCount; // Referans = 3

  I2 := nil;
  iRefCount := TTest(Test).RefCount; // Referans = 2

  I3 := nil;
  iRefCount := TTest(Test).RefCount; // Referans = 1
end;
</pre>
<p>Örneğimiz de ITest isimli herhangi bir metoda sahip olmayan bir interface ve o interface&#8217;i implemente etmiş bir basit sınıfımız var.  Test değişkenimizin ve I1, I2, I3 değişkenlerimizin interface türünde olduğuna dikkat ediniz. Test := TTest.Create satırının işletilmesi hemen IInterface arabiriminin içinde tanımlı olan ve TInterfacedObject&#8217;de yeniden tanımlanan <span style="color: #ff9900;">_AddRef</span> isimli metoda dallanılmasını sağlar. Aynı şekilde I1 := Test; ataması, _AddRef metodunun yeniden çağırılmasına, dolayısı ile Test değişkeninin referans değerini 1 arttırmasına neden olur. TInterfacedObject sınıfının <span style="color: #ff9900;">_Addref, _Release ve QueryInterface</span> metodlarını nasıl uyguladığına biraz daha yakinen bakalım:</p>
<pre class="brush: delphi">
function TInterfacedObject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj) then
    Result := 0
  else
    Result := E_NOINTERFACE;
end;

function TInterfacedObject._AddRef: Integer;
begin
  Result := InterlockedIncrement(FRefCount);
end;

function TInterfacedObject._Release: Integer;
begin
  Result := InterlockedDecrement(FRefCount);
  if Result = 0 then
    Destroy;
end;
</pre>
<p> Gördüğünüz gibi TInterfacedObject sınıfının _AddRef metodunda FRefCount isimli değişken Thread güvenlikli olarak(<span style="color: #ff9900;">InterlockedIncrement</span>) 1 arttırılıyor. _Release metodunda ise Thread güvenlikli olarak(<span style="color: #ff9900;">InterlockedDecrement</span>) 1 azaltılıyor. FRefCount değerinin 0&#8242;a düşmesi ilgili nesneye herhangi bir referans kalmaması anlamına geldiği için otomatikman <span style="color: #ff9900;">Destroy</span> metodu çağrılıyor.</p>
<p> Yukarıdaki örneğimizde Test : ITest tanımı global alanda yapıldığı için Destroy metodu çağrılmayacaktır. Ancak bu tanım, lokal olarak yapılsa idi ilgili procedure&#8217;den çıkıldığında otomatikman referans 1 azaltılacağından Destroy metodu çağrılabilecek ve nesnemiz yok edilecekti.! Bir interface değişkenine nil atamasının yapılması, yada o değişkenin kullanıldığı kod bloğundan çıkılması(scope&#8217;un dışına çıkılması) ilgili interface referansını 1 azaltan _Release çağrısına neden olur. Bu sayede gerçek sınıflarınıza interface değişkenlerle ulaşmanız, bellek yönetimini otomatik yapmanız anlamını taşır. Yine yukarıdaki örneğimiz için konuşacak olursak, lokal olarak tanımlanmış I1, I2 ve I3 değişkenlerine nil atamasının yapılmadığı düşünülürse, procedure&#8217;den çıkıldığında her bir değişken için otomatikman nil ataması yapılacak ve referanslar yine düzgün bir şekilde azaltılabilecektir.</p>
<p> Tüm bu yazdıklarımızdan sonra Interface&#8217;lerin referans sayma mekanizmasının memory leak&#8217;ler hususunda bizlere yardımcı olabildiğini görebiliyoruz. Aynı zamanda Interface&#8217;ler dilde native olarak bulunmayan multiple inheritance hususunda da bizlere yardımcı olabilmekteydi. Yukarıda tanımını verdiğimiz IInterface arabiriminin QueryInterface adlı metoduna şu ana dek hiç değinmedik. QueryInterface metodu bir nesnenin belirtilen bir interface&#8217;i implemente edip etmediğini sorgulamak için kullanılır. Örneğimizde kullandığımız gibi bazen interface referanslarımızdan nesne referanslarına dönüşüm yapmak isteriz. Bunun için ya casting uygularız yada as operatörünü kullanırız. Her iki durumda da TInterfacedObject sınıfının QueryInterface metoduna dallanılır.</p>
<p> Görüldüğü gibi aslında interface&#8217;ler ve abstract sınıflar birbirlerine benzer özelliklere sahip. Abstract sınıfların kalıtımdan yararlanmak, erişim belirleyicilerini kullanabilmek, değişken tanımlamalarına sahip olmak gibi özellikleri olmasına rağmen; Interface&#8217;ler de tüm bu özelliklere haiz olmamalarına rağmen multiple inheritance&#8217;a imkan sağlamak ve referans sayımı metodunun uygulanması sebebi ile otomatik yok etme mekanizmasının güzelliklerini bizlere sunabilmesi adına önemli bir kullanım alanına sahiptir.</p>
<p> Herhangi bir interface&#8217;i implemente etmiş olan bir sınıfa bir interface değişkeni vasıtası ile ulaşıyorsanız, o sınıfın yok edilmesi ile ilgilenmenize gerek kalmaz. Delphi sizler için bunu mükemmel bir şekilde yapabilir. Interface&#8217;ler ile ilgili konuşulacak daha başka hususlar da olabilir, ancak bu hususları anlatmak yerine sizlerin araştırmalarına ve testlerine bırakmak öğrenme yolunda faydalı bir adım olabilir.</p>
<p> Interface&#8217;lerle ilgili kısıtlı miktardaki bilgimi sizlerle paylaştıktan sonra, bir sonraki adımımız Operator Overloading ve ardından da Delphi ortamında istisnasız her sınıfı otomatikman yok edebilme becerisine sahip olacak bir tasarımı sizinle paylaşmak olacaktır.</p>
<p>Saygılar, sevgiler..</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tugrulhelvaci.com/?feed=rss2&amp;p=582</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Derinlemesine Threading..(3)</title>
		<link>http://www.tugrulhelvaci.com/?p=568</link>
		<comments>http://www.tugrulhelvaci.com/?p=568#comments</comments>
		<pubDate>Sat, 03 Apr 2010 02:19:29 +0000</pubDate>
		<dc:creator>Tuğrul HELVACI</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Win32]]></category>
		<category><![CDATA[İşletim Sistemi]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[GetThreadContext]]></category>
		<category><![CDATA[ResumeThread]]></category>
		<category><![CDATA[SetThreadContext]]></category>
		<category><![CDATA[SuspendThread]]></category>
		<category><![CDATA[TerminateThread]]></category>
		<category><![CDATA[Thread]]></category>

		<guid isPermaLink="false">http://www.tugrulhelvaci.com/?p=568</guid>
		<description><![CDATA[ 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&#8217;i normal yollarla durdurabilmek için(TerminateThread API&#8217;sini kullanmadan) bir makale yazacağımdan bahsetmiştim. Bu makalemizin konusu verdiğim söz gereği; bir thread&#8217;i sonlandırma seçenekleri ile ilgili olacak.
Ancak, sizlerden ricam bu makalede ilerlemeden önce Threading [...]]]></description>
			<content:encoded><![CDATA[<p> Threading ile ilgili <a href="http://www.tugrulhelvaci.com/?p=528">bir önceki makelemizde</a> Event ve WaitableTimer senkronizasyon mekanizmalarını anlatmış ve konuya ışık tutabilmesi adına örnekler paylaşmıştık. Makalemizin sonunda ise; bir thread&#8217;i normal yollarla durdurabilmek için(<span style="color: #ff9900;">TerminateThread API&#8217;sini kullanmadan</span>) bir makale yazacağımdan bahsetmiştim. Bu makalemizin konusu verdiğim söz gereği; bir thread&#8217;i sonlandırma seçenekleri ile ilgili olacak.</p>
<p>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:</p>
<p><a href="http://www.tugrulhelvaci.com/?p=126">Nedir bu Thread’lerden çektiğimiz..!</a><br />
<a href="http://www.tugrulhelvaci.com/?p=443">Derinlemesine Threading..(1)</a><br />
<a href="http://www.tugrulhelvaci.com/?p=528">Derinlemesine Threading..(2)</a></p>
<p> Şimdi tüm bu makaleleri okuduğunuzu ve threading hakkında fikriniz olduğunu varsayarak, bir thread&#8217;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&#8217;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&#8217;lerimizin içindeki kodlar uzun süreli ve hatta kullanıcı ile interaktif çalışıyor da olabilir. </p>
<p>Bazı durumlarda, yazdığımız thread&#8217;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.</p>
<p> Pek çok programcı bu gibi durumlarda, çalışan thread&#8217;ini durdurabilmek için <span style="color: #ff9900;">TThread</span> sınıfının <span style="color: #ff9900;">Terminated</span> özelliğini kontrol eder. Terminated&#8217;in true olması durumunda thread çalışma kodundan çıkılmasını sağlar. Buna küçük bir örnek verebiliriz;</p>
<pre class="brush: delphi">
procedure TMyThread.Execute;
begin
  inherited;

  while not Terminated do
  begin
     //...
     //...
  end;
end;
</pre>
<p> Yukarıdaki örnek, sıklıkla kullanılan çok genel bir örnektir. Malumunuz olduğu üzere; TThread sınıfının <span style="color: #ff9900;">Terminate</span> metodu bir thread&#8217;i durdurma işini <strong>yapmaz</strong>. Terminate metodu; Terminated isimli property&#8217;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&#8217;in sonlanması anlamını taşır.</p>
<p> 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&#8217;lerimiz içinde yukarıdaki kısa kod örneğinde olduğu gibi Terminated property&#8217;sinden istifade edemeyiz. Bu hususta da kısa bir örnek verip devam etmek sanırım daha açıklayıcı olacaktır:<br />
<span id="more-568"></span></p>
<pre class="brush: delphi">
procedure TMyThread.Execute;
begin
  inherited;

  with TADOStoredProc.Create(nil) do
  begin
    Connection := myThreadSpecificConnection;
    ProcedureName := &#039;sp_Stok_Hareket_List&#039;;
    Parameters.Refresh;
    Open; // Tüm stokların çekildiğini ve stok hareket tablosunda 10 milyon kayıt olduğunu düşünelim.

    //...
    //...
  end;
end;
</pre>
<p> Yukarıdaki örneğimiz; gerçek hayatta sıklıkla kullandığımız bir yapıya işaret ediyor. Örneğimizde; bir thread içinden Stok Hareket Raporu almaya çalışıyoruz. Ve Stok Hareket tablomuzdaki kayıt sayısının da 10 milyon olduğunu varsaydık. Bu gibi bir durumda; <em>Open</em> çağrısı en iyi ihtimalle onlarca dakika sürecektir. İşte tam bu noktada, programınızı kullanan kullanıcının; raporun alınmasının uzun sürmesinden mütevellit işlemden sıkılması ve iptal etmesi isteği hasıl olursa ne yapabiliriz ?</p>
<p> Bütün iş yükünü oluşturan kod TADOStoredProc&#8217;un Open metodunun içinde olduğu için; Terminated vb. kontrolleri kullanma imkanına sahip değiliz. Bu gibi zamanlarda, karşımıza bu makaleye konu olacak derin hususlar devreye girecektir.</p>
<p> Bir thread&#8217;i sonlandırabilmek için işletim sistemi API&#8217;lerinde bizlere sunulan bir kaç seçenek vardır. Bunlar; <span style="color: #ff9900;">TerminateThread</span> ve <span style="color: #ff9900;">ExitThread</span> API&#8217;leridir. Bu API&#8217;lerin ne işe yaradıklarını anlatmadan önce her zamanki gibi tanımlarına bir göz gezdirelim:</p>
<pre class="brush: c++">
  VOID ExitThread(
    DWORD dwExitCode 	// exit code for this thread
   );

  BOOL TerminateThread(
    HANDLE hThread,	// handle to the thread
    DWORD dwExitCode 	// exit code for the thread
   );
</pre>
<p>ve Delphi tanımlarımız:</p>
<pre class="brush: delphi">
  procedure ExitThread(dwExitCode: DWORD);
  function TerminateThread(hThread: THandle; dwExitCode: DWORD): BOOL;
</pre>
<p>ExitThread API&#8217;si o anda çalışan uygulamanın içindeki thread&#8217;lerin <strong>hangisinde çağırılıyor ise</strong> o thread&#8217;den çıkmak için kullanılır. Yukarıdaki örneğimizde; TMyThread sınıfının Execute metodunda Open&#8217;dan sonra ExitThread API&#8217;sini kullanmak yine bizim istediğimiz neticeyi vermeyecektir.</p>
<p> ExitThread API&#8217;sini uygulamamızın main thread&#8217;inde çağırmak demek uygulamamızın kapanması anlamına geleceğinden diğer API&#8217;mize bakmak durumunda olacağız. TerminateThread ise kendisine geçilen bir thread handle vasıtası ile ilgili thread&#8217;i sonlandırabilme yeteneğine sahiptir.</p>
<p> Bizim istediğimiz işi TerminateThread API&#8217;si yapabildiğine göre o halde bu makaleye ne gerek vardı diye düşünüyor olabilirsiniz ?</p>
<p>Elbette bunun çok yerinde nedenleri var. TerminateThread API&#8217;sinin <strong>kullanımı son derece tehlikelidir.</strong> Bir thread içinde senkronizasyon nesneleri kullanıyorsanız(Critical Section, Event, Semaphore, Mutex vb.) TerminateThread çağrısından sonra thread&#8217;inizin sonlandırılma kodları çağırılamayacağı için, ilgili senkronizasyon nesneleriniz sürekli açık yada kapalı konumlarında kalabilir. Bu da, bu senkronizasyon nesnelerini ortak kullanan diğer thread&#8217;leriniz için son derece büyük bir sorun teşkil edebilir. Aynı zamanda; TerminateThread ile yapacağınız sonlandırmalar; thread içinde oluşturduğunuz nesnelerinizin Free edilememesi, ayırdığınız hafıza bloklarının işletim sistemine iade edilememesi gibi ciddi sorunlara neden olabilir.</p>
<p> İşte bu sebeplerden ötürü, gerçekten çok ihtiyacınız yoksa <strong>TerminateThread API&#8217;sinin kullanılması tavsiye edilmez.!</strong> Peki bu durumda, biz thread&#8217;lerimizi sonlandıramayacak mıyız ?</p>
<p>Evet, sonlandırabileceğiz. Ancak, biraz karmaşık bir yöntem kullanmamız gerekecek. Bu yöntemi izaha geçmeden önce; diğer threading mekanizmalarında gördüklerinizi yeniden anımsamanız gerekecek. Hatırlayacağınız üzere, işletim sistemi aynı anda sadece bir tane işlem yapabilmektedir. Thread&#8217;lerin birbirleri arasındaki geçişlerinin 20 ms. olduğunu ve bu geçişler için Round Robin denilen algoritmanın kullanıldığını sanırım anımsıyorsunuz.</p>
<p> Tam bu noktada; ilgili threading makalelerinde değinmediğimiz bir soru sormamız gerekiyor kendimize. İşletim sistemi thread&#8217;ler arasında geçişler yaparken; thread&#8217;lere has local değişkenlerin bilgilerini, o anda thread execute kodunun hangi satırında kaldığını, hangi nesnelerle işlem yaptığını ve o nesnelerin hafıza adreslerinin ne olduğunu nereden biliyor olabilir ?</p>
<p> Eğer işletim sistemi thread&#8217;lerle alakalı tüm bu bilgilere haiz olmasa idi, thread&#8217;ler arasında geçişi sağlayamaz; sağlasa da CPU son işlediği komutu bilemeyeceği için gerçek bir threading&#8217;den söz edilemezdi.</p>
<p>Sizlere bu hususu izah edebilmem için az da olsa <strong>assembly </strong>bilgisine  haiz olmanız gerekmekte. Bildiğiniz gibi; tüm programlama dillerinde üretilen çalıştırılabilir dosyalar işlemci assembly kodlarına dönüştürülür ve öyle icra edilir. Olayın derinliklerinde CPU&#8217;nun sadece 0(Sıfır) ve 1(Bir) lerden anladığı da bir gerçekken, assembly kodları da makina kodlarına dönüştürülür.</p>
<p>Kabaca; CPU&#8217;nun çalışacak kodları işletebilmesi için çeşitli <span style="color: #ff9900;">register</span>&#8216;lara ihtiyacı vardır. Hemen hemen hepimiz az da olsa bu register&#8217;lara aşinayızdır. acx, ecx, ah, al, eip, cs, ds gibi registerları en azından görmüş yada bunlar hakkında bir genel kültüre sahibizdir. </p>
<p>Delphi programlama ortamında <strong>CPU Editoru</strong> aşağıdaki gibi görünür. Sizlerinde aşağıdaki resimde gözlemleyebileceğiniz gibi; pek çok register ve assembly kodları görünmekte. Şimdilik sadece <span style="color: #ff9900;">EIP register</span>&#8216;ının içindeki değerin(<strong>0045187C</strong>) sol taraftaki <em>Memo1.Lines.Add</em> kod satırındaki numara ile aynı olduğunu farketmenizi rica ediyorum.</p>
<p><a href="http://www.tugrulhelvaci.com/wp-content/uploads/CPU_Editor.PNG"><img src="http://www.tugrulhelvaci.com/wp-content/uploads/CPU_Editor.PNG" alt="CPU Editor" title="CPU Editor" width="550" height="300" class="aligncenter size-full wp-image-409" /></a></p>
<p> Örneğimizde ki <strong>0045187C</strong> bir hafıza adresidir ve o anda CPU tarafından işletilen kodun nerede olduğunu bulmaya yarar. CPU bu değere her zaman EIP registerı içinden erişir. Satır satır işletilen her kod&#8217;un hafıza edresi EIP registerı içerisinde tutulur.</p>
<p> Tüm bu bilgileri vermemizin nedeni; işletim sisteminin thread geçişlerinde yukarıda bir kısmını gözlemleyebildiğiniz pek çok CPU register&#8217;ını bir yere saklıyor olmasından ve thread&#8217;e geri dönüşte ilgili register&#8217;ların içeriklerini sakladığı yerden yeniden okuyup eski haline almasından ötürüdür. İşletim sistemi thread geçişlerinde ihtiyacı olan tüm bilgileri <span style="color: #ff9900;">_CONTEXT</span> isimli bir record&#8217;un içine doldurur ve bir yerlerde bu record&#8217;u saklar. Thread&#8217;e geri dönmesi gerektiğinde sakladığı yerdeki bilgileri _CONTEXT recordunun içerisine geri yükler ve bu sayede CPU kaldığı noktadan kodları işletmeye devam edebilir. </p>
<p>İzahatlarımızda daha fazla ilerlemeden evvel, bu recordun yapısını sizlerle paylaşayım:</p>
<pre class="brush: delphi">
  _CONTEXT = record
  {$EXTERNALSYM _CONTEXT}

  { The flags values within this flag control the contents of
    a CONTEXT record.

    If the context record is used as an input parameter, then
    for each portion of the context record controlled by a flag
    whose value is set, it is assumed that that portion of the
    context record contains valid context. If the context record
    is being used to modify a threads context, then only that
    portion of the threads context will be modified.

    If the context record is used as an IN OUT parameter to capture
    the context of a thread, then only those portions of the thread&#039;s
    context corresponding to set flags will be returned.

    The context record is never used as an OUT only parameter. }

    ContextFlags: DWORD;

  { This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
    set in ContextFlags.  Note that CONTEXT_DEBUG_REGISTERS is NOT
    included in CONTEXT_FULL. }

    Dr0: DWORD;
    Dr1: DWORD;
    Dr2: DWORD;
    Dr3: DWORD;
    Dr6: DWORD;
    Dr7: DWORD;

  { This section is specified/returned if the
    ContextFlags word contians the flag CONTEXT_FLOATING_POINT. }

    FloatSave: TFloatingSaveArea;

  { This section is specified/returned if the
    ContextFlags word contians the flag CONTEXT_SEGMENTS. }

    SegGs: DWORD;
    SegFs: DWORD;
    SegEs: DWORD;
    SegDs: DWORD;

  { This section is specified/returned if the
    ContextFlags word contians the flag CONTEXT_INTEGER. }

    Edi: DWORD;
    Esi: DWORD;
    Ebx: DWORD;
    Edx: DWORD;
    Ecx: DWORD;
    Eax: DWORD;

  { This section is specified/returned if the
    ContextFlags word contians the flag CONTEXT_CONTROL. }

    Ebp: DWORD;
    Eip: DWORD;
    SegCs: DWORD;
    EFlags: DWORD;
    Esp: DWORD;
    SegSs: DWORD;
  end;
</pre>
<p> Gördüğünüz gibi pek çok üyesi var. Ancak hemen endişeye kapılmayın. Bizler sadece EIP üyesi ile ilgileniyor olacağız. Record&#8217;un diğer üyeleri şu an için bizim konumuz dışında olması münasebeti ile bu üyeler hakkındaki kısıtlı bilgilerimi sizlerle paylaşmıyorum.</p>
<p> Tüm bu açıklamalardan sonra; bir thread&#8217;i nasıl sonlandırabileceğimiz hakkında hâla kafanızda soru işaretleri olduğunu hissediyorum. Biz bunun için; genellikle <em>Reverse Engineering</em> konularından birisinin kapsamına gireceğiz. <span style="color: #ff9900;">Yani bir thread&#8217;in o anda işletmekte olduğu kodun adresini bir başka metodun adresi ile değiştireceğiz.</span> Yapacağımız işin sırrı da tam olarak bu noktada. </p>
<p> Kısaca ve kabaca ifade etmek gerekir ise; thread&#8217;in zaman alan bir işlemi yürütüyor olduğu sırada EIP registeri içindeki CPU&#8217;nun işlettiği kodun adresini bir başka kod adresi ile değiştireceğiz. Böylece CPU&#8217;yu kandırmış olacağız ve thread&#8217;in kodları içine bir nevi <strong>code injection</strong> yapmış olacağız. Bu senaryoyu aşağıdaki pseudo kod ile örnekleyebiliriz:</p>
<pre class="brush: delphi">
procedure TMyThread.Execute;
begin
  inherited;

  with TADOStoredProc.Create(nil) do
  begin
    Connection := myThreadSpecificConnection;
    ProcedureName := &#039;sp_Stok_Hareket_List&#039;;
    Parameters.Refresh;
    Open; // Tüm stokların çekildiğini ve stok hareket tablosunda 10 milyon kayıt olduğunu düşünelim.

    //...
    //...
  end;
end;

procedure TForm1.Button1Click(Sender : TObject);
begin
  Change_EIP_Register(myThread, @MyProcedure);
end;

procedure MyProcedure;
begin
  raise Exception.Create(&#039;Thread dursun artık..!&#039;);
end;

procedure Change_EIP_Register(Thread : TThread; ProcedureAddr : Pointer);
begin
  // Thread&#039;i durdur(Suspend)
  // Thread&#039;in Context record bilgilerini al.
  // Context recordunun içindeki EIP üyesinin değerini ProcedureAddr ile değiştir.
  // Yeni context recordunu thread&#039;in eski context&#039;i ile değiştir.
  // Thread&#039;i yeniden çalıştır(Resume)
end;
</pre>
<p> Yukarıdaki pseudo kodda görebileceğiniz gibi öncelikle thread&#8217;imizi durdurmamız icap ediyor. Context bilgilerinin alınabilmesi için bu şart. Ardından Context record&#8217;u içindeki EIP registerımızın içinde bulunan o anda çalışmakta olan kod satırının adresini; yeni tanımladığımız MyProcedure ile değiştiriyor ve thread context&#8217;imizi güncelleyip thread&#8217;imizi yeniden çalıştırıyoruz. Bu aşamada; CPU direkt olarak MyProcedure metodunun adresini EIP registerından okur ve ilgili kodları çalıştırmaya başlar. MyProcedure metodunun içinde bir exception oluşturduğumuz içinde thread&#8217;imiz anında sonlanacaktır ve bu vesile ile thread sonlandırma kodlarımız da gerektiği gibi işletilebilecektir(OnTerminate).</p>
<p> Şimdi sıra bize gereken API&#8217;lerin tanımlarına geldi:</p>
<pre class="brush: c++">
  DWORD SuspendThread(
    HANDLE hThread 	// handle to the thread
   );

  BOOL GetThreadContext(
    HANDLE hThread,	// handle of thread with context
    LPCONTEXT lpContext 	// address of context structure
   );

  BOOL SetThreadContext(
    HANDLE hThread,	// handle of thread with context
    CONST CONTEXT *lpContext 	// address of context structure
   );

  DWORD ResumeThread(
    HANDLE hThread 	// identifies thread to restart
   );
</pre>
<p>ve Delphi tanımları:</p>
<pre class="brush: delphi">
  function SuspendThread(hThread: THandle): DWORD;
  function GetThreadContext(hThread: THandle; var lpContext: TContext): BOOL;
  function SetThreadContext(hThread: THandle; const lpContext: TContext): BOOL;
  function ResumeThread(hThread: THandle): DWORD;
</pre>
<p>Tanımlarımızı da sizlerle paylaştığımıza göre; artık örnek kodumuzu yazabiliriz:</p>
<pre class="brush: delphi">
var
  mThread : TMyThread;

implementatiton

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

procedure TMyThread.Execute;
begin
  inherited;

  with TADOStoredProc.Create(nil) do
  begin
    Connection := myThreadSpecificConnection;
    ProcedureName := &#039;sp_Stok_Hareket_List&#039;;
    Parameters.Refresh;
    Open; // Tüm stokların çekildiğini ve stok hareket tablosunda 10 milyon kayıt olduğunu düşünelim.

    //...
    //...
  end;
end;

procedure TForm1.ThreadTerminated(Sender : TObject);
begin
  Memo1.Lines.Add(&#039;Bitiş Zamanı:&#039; + TimeToStr(Time)); // Memo1 TMemo türündedir.
end;

procedure TForm1.StartThreadClick(Sender : TObject); // StartThread TButton türündedir.
begin
  Memo1.Lines.Add(&#039;Başlangıç Zamanı:&#039; + TimeToStr(Time)); // Memo1 TMemo türündedir.

  mThread := TMyThread.Create;
  mThread.OnTerminate := ThreadTerminated;
  mThread.Resume;
end;

procedure StopTheThread;
begin
  raise Exception.Create(&#039;Thread terminated by using Context Record EIP Register..&#039;);
end;

procedure TForm1.StopThreadClick(Sender : TObject); // StopThread TButton türündedir.
var
  ctx : _CONTEXT;
  ThreadHandle : THandle;
begin
  ThreadHandle := mThread.Handle;

  SuspendThread(ThreadHandle); // Thread&#039;i durdur..
  ctx.ContextFlags := CONTEXT_FULL; // Tüm Context bilgisini istedik.
  GetThreadContext(ThreadHandle, ctx);

  ctx.Eip := Cardinal(@StopTheThread);

  SetThreadContext(ThreadHandle, ctx);
  ResumeThread(ThreadHandle); // Thread&#039;i başlat..
end;
</pre>
<p> Yukarıdaki kodu çalıştırıp denemelerinizi yaptığınızda uzun süren thread&#8217;inizin sonlandırıldığını ve sonlandırılma kodlarının da düzgün bir şekilde çalışabildiğini gözlemleyebilirsiniz. Bu yöntem, biraz karmaşık ve uzun olsa da; thread sonlandırmanız gerektiğinde kullanmanız icap eden en önemli yöntemdir. TerminateThread API&#8217;sinin dezavantajlarının hiçbirisine sahip olmaması programlarınızın daha güvenli çalışabilmesi adına büyük bir avantajdır.</p>
<p> Elbette bu yöntem kötüye de kullanılabilir, EIP register&#8217;ının içindeki değer değiştirilmeden önce bir başka değişkende saklanırsa ve ardından değiştirilir ve thread yeniden çalıştırılırsa; buna mukabil eski EIP değeri yeniden context&#8217;e atanırsa bir thread&#8217;in içine code injection yapmış olursunuz. Buna da küçük bir örnek vererek makalemi neticelendirmek istiyorum müsaadeniz ile:</p>
<pre class="brush: delphi">
procedure StopTheThread;
begin
  Memo1.Lines.Add(&#039;Thread durduruldu.!&#039;);
end;

procedure TForm1.StopThreadClick(Sender : TObject); // StopThread TButton türündedir.
var
  ctx : _CONTEXT;
  ThreadHandle : THandle;
  OldEIP : Cardinal;
begin
  ThreadHandle := mThread.Handle;

  SuspendThread(ThreadHandle);
  ctx.ContextFlags := CONTEXT_FULL; // Tüm Context bilgisini istedik.
  GetThreadContext(ThreadHandle, ctx);
  OldEIP := ctx.EIP;
  ctx.Eip := Cardinal(@StopTheThread);

  SetThreadContext(ThreadHandle, ctx);
  ResumeThread(ThreadHandle);

  Sleep(5000); // 5 sn. yeni kodumuzun çalışmasını bekleyelim ve eski çalışma şekline geri dönelim.

  SuspendThread(ThreadHandle);
  ctx.ContextFlags := CONTEXT_FULL; // Tüm Context bilgisini istedik.
  GetThreadContext(ThreadHandle, ctx);
  ctx.Eip := OldEIP;

  SetThreadContext(ThreadHandle, ctx);
  ResumeThread(ThreadHandle);
end;
</pre>
<p>Kötü amaçlarla kullanmamanız dileği ile <img src='http://www.tugrulhelvaci.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>Saygılar, sevgiler..</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tugrulhelvaci.com/?feed=rss2&amp;p=568</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>SQL&#8217;de Benzerlik Algoritmaları&#8230;</title>
		<link>http://www.tugrulhelvaci.com/?p=559</link>
		<comments>http://www.tugrulhelvaci.com/?p=559#comments</comments>
		<pubDate>Fri, 02 Apr 2010 11:39:54 +0000</pubDate>
		<dc:creator>selomania77</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Algoritma]]></category>
		<category><![CDATA[Smilarity]]></category>

		<guid isPermaLink="false">http://www.tugrulhelvaci.com/?p=559</guid>
		<description><![CDATA[EDIT DISTANCE
Aşağıdaki 2 fonksiyon size stringler arasındaki benzerlik oranlarını vermekte en benzer olanların sonuçları en küçük olanlar çıkacaktır. Yani fark arttıkça dönen sayı artmakta bunu unutmayın.


CREATE FUNCTION edit_distance(@s1 nvarchar(3999), @s2 nvarchar(3999))
RETURNS int
AS
BEGIN
  DECLARE
     @s1_len int,
     @s2_len int,
     @i int,
    [...]]]></description>
			<content:encoded><![CDATA[<p><strong>EDIT DISTANCE</strong></p>
<p>Aşağıdaki 2 fonksiyon size stringler arasındaki benzerlik oranlarını vermekte en benzer olanların sonuçları en küçük olanlar çıkacaktır. Yani fark arttıkça dönen sayı artmakta bunu unutmayın.<br />
<span id="more-559"></span></p>
<pre class="brush: sql">
CREATE FUNCTION edit_distance(@s1 nvarchar(3999), @s2 nvarchar(3999))
RETURNS int
AS
BEGIN
  DECLARE
     @s1_len int,
     @s2_len int,
     @i int,
     @j int,
     @s1_char nchar,
     @c int,
     @c_temp int,
     @cv0 varbinary(8000),
     @cv1 varbinary(8000)

  SELECT
    @s1_len = LEN(@s1),
    @s2_len = LEN(@s2),
    @cv1 = 0x0000,
    @j = 1,
    @i = 1,
    @c = 0

   WHILE @j &lt;= @s2_len
     SELECT
       @cv1 = @cv1 + CAST(@j AS binary(2)),
       @j = @j + 1

  WHILE @i &lt;= @s1_len
  BEGIN
    SELECT
      @s1_char = SUBSTRING(@s1, @i, 1),
      @c = @i,
      @cv0 = CAST(@i AS binary(2)),
      @j = 1

    WHILE @j &lt;= @s2_len
    BEGIN
      SET @c = @c + 1
      SET @c_temp = CAST(SUBSTRING(@cv1, @j+@j-1, 2) AS int) +
        CASE
          WHEN @s1_char = SUBSTRING(@s2, @j, 1) THEN 0 ELSE 1
        END

      IF @c &gt; @c_temp SET @c = @c_temp
      SET @c_temp = CAST(SUBSTRING(@cv1, @j+@j+1, 2) AS int)+1

      IF @c &gt; @c_temp SET @c = @c_temp
      SELECT @cv0 = @cv0 + CAST(@c AS binary(2)), @j = @j + 1
    END

  SELECT @cv1 = @cv0, @i = @i + 1
  END
RETURN @c
END
</pre>
<p><strong>LEVINSTEIN ALGORITMASI</strong></p>
<pre class="brush: sql">
CREATE FUNCTION dbo.fn_LevenshteinDistance
(
  @Str1 VARCHAR(3999)
, @Str2 VARCHAR(3999)
)
RETURNS INT
AS
BEGIN
/*
#
SELECT dbo.fn_LevenshteinDistance(&#039;test&#039;, &#039;test&#039;)
#
SELECT dbo.fn_LevenshteinDistance(&#039;tesot&#039;, &#039;test&#039;)
#
SELECT dbo.fn_LevenshteinDistance(&#039;testing1&#039;, &#039;tes_ing&#039;)
#
*/

  DECLARE @m INT, @n INT
  DECLARE @editMatrix TABLE(pk1 INT, pk2 INT, v INT, PRIMARY KEY (pk1, pk2))
  DECLARE @i INT, @j INT, @cost INT
  DECLARE @str1_i CHAR, @str2_j CHAR

  SET @n = DATALENGTH(ISNULL(@Str1, &#039;&#039;))
  SET @m = DATALENGTH(ISNULL(@Str2, &#039;&#039;))

  IF @n = 0 OR @m = 0 RETURN 0

  SET @i = 0
  WHILE @i &lt;= @n
  BEGIN
    SET @j = 0
    WHILE @j &lt;= @m
    BEGIN
      IF @j = 0
        INSERT INTO @editMatrix VALUES(@i, 0, @i)
      ELSE IF @i = 0
        INSERT INTO @editMatrix VALUES(0, @j, @j)
      ELSE
        INSERT INTO @editMatrix VALUES(@i, @j, 0)

      SET @j = @j + 1
    END

    SET @i = @i + 1
  END

  SET @i = 1
  WHILE @i &lt;= @n
  BEGIN
    SET @str1_i = SUBSTRING(@Str1, @i, 1)

    SET @j = 1
    WHILE @j &lt;= @m
    BEGIN
      SET @str2_j = SUBSTRING(@Str2, @j, 1)

      IF @str1_i = @str2_j
        SET @cost = 0
      ELSE
        SET @cost = 1

      UPDATE em
      SET v = (
        SELECT MIN(ret) AS MIN_ret
        FROM (
          SELECT v + 1 AS ret
          FROM @editMatrix
          WHERE pk1 = @i - 1 AND pk2 = @j     

          UNION ALL

          SELECT v + 1 AS ret
          FROM @editMatrix
          WHERE pk1 = @i AND pk2 = @j - 1 

         UNION ALL

         SELECT v + @cost AS ret
         FROM @editMatrix
         WHERE pk1 = @i - 1 AND pk2 = @j - 1
       ) AS x
    )
      FROM @editMatrix AS em
      WHERE pk1 = @i AND pk2 = @j

      SET @j = @j + 1
    END

    SET @i = @i + 1
  END

 RETURN (SELECT ISNULL(v, -1) FROM @editMatrix WHERE pk1 = @n AND pk2 = @m)
END
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.tugrulhelvaci.com/?feed=rss2&amp;p=559</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Full Text Searching&#8230;</title>
		<link>http://www.tugrulhelvaci.com/?p=558</link>
		<comments>http://www.tugrulhelvaci.com/?p=558#comments</comments>
		<pubDate>Fri, 02 Apr 2010 11:35:28 +0000</pubDate>
		<dc:creator>selomania77</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[MSSQL]]></category>
		<category><![CDATA[Query]]></category>
		<category><![CDATA[SORT]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://www.tugrulhelvaci.com/?p=558</guid>
		<description><![CDATA[Full-Text Search ile Arama İşlemleri
Full Text Search servisi ile SQL Server&#8221;da karakter bazlı alanlarda arama yapılarak daha verimli arama sonuçları elde edilebilir, ancak arama yapılacak olan alanlar üzerinde ilk olarak Full-Text Index&#8221;ler tanımlanmalıdır. Full Text Index&#8221;ler belirtilen alanın verisini fiziksel olarak belirtilen alana indeksleyerek bu alan içerisinde arama yapılmasını kolaylaştırır. Full Text Index tanımlamak için [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Full-Text Search ile Arama İşlemleri</strong></p>
<p>Full Text Search servisi ile SQL Server&#8221;da karakter bazlı alanlarda arama yapılarak daha verimli arama sonuçları elde edilebilir, ancak arama yapılacak olan alanlar üzerinde ilk olarak Full-Text Index&#8221;ler tanımlanmalıdır. Full Text Index&#8221;ler belirtilen alanın verisini fiziksel olarak belirtilen alana indeksleyerek bu alan içerisinde arama yapılmasını kolaylaştırır. Full Text Index tanımlamak için indeksin tanımlanacak olduğu tablo üzerine sağ tıklayarak Full Text Index seçeneği üzerinden Define Full Text Index seçeneği seçilmelidir. Full-Text Index tanımlarken bu Full-Text Indexler&#8221;in gruplanacağı yeni bir Full-Text Catalog tanımlanacağı gibi var olan  bir kataloga da eklenmesi sağlanabilir. Full-Text Index tanımlanırken dikkat edilmesi gereken bir nokta da indeksin tanımlanacak olduğu tabloda en azından bir tane UniqueKey tanımlanmış olması gerekliliğidir. Tabi bu ayarı yapabilmek için DataBase&#8221;de Full Text Indexing özelliğinin aktif olarak ayarlanmış olması gerekmektedir. Eğer üzerinde arama yapılmak istenilen DataBase&#8221;in Full-Text Indexing özelliği aktif değilse DataBase&#8221;in özelliklerinden Files bölümünde bulunan CheckBox seçili hale getirilerek aktif yapılabilir. Full-Text Index&#8221;ler char, varchar, text, ntext, nchar ve nvarchar veri tipinde tanımlanmış olan alanlar üzerinde tanımlanabilir ve Full Text Search servisi bu alanlarla kullanılabilir.<br />
Arama yapılacak olan sütun ya da sütunlar üzerinde Full-Text Search servisi kullanılarak arama yapmak için kullanılabilecek bir kaç farklı SQL sözcüğü vardır bu yazımda bunlardan CONTAINS ve FREETEXT sözcükleri üzerinde örnekleme yapıyor olacağım.<br />
<span id="more-558"></span><br />
<strong>CONTAINS</strong><br />
Contains sözcüğü ile hedef alan içerisinde arama yapılacak olan sözcüklerin içinde geçtiği kayıtlar ve bu sözcüğe yakın olan kayıtlar listeleniyor olacaktır. Bu alanda örnek vermek gerekirse drive sözcüğünü ele aldığımızda sorgu sonucunda drive, drove, drives, driving ve diriven gibi sözcükleri içeren kayıtlarda sorgu sonucunda yer alıyor olacaktır. Contains sözcüğünün kullanımını aşağıda yer almaktadır.</p>
<pre class="brush: sql">
SELECT Description
FROM Production.ProductDescription
WHERE CONTAINS ( *, &quot; &quot;For true&quot; &quot; )
</pre>
<p>Contains sözcüğü kullanılırken ilk olarak * işareti ile tablo üzerinde tanımlı olan tüm Full-Text Index&#8221;lerde arama yapılacağı belirtiliyor, bu alanda istenilirse indeks tanımlı olan sütunlardan istenilenlerde verilebilirdi. Diğer alanda ise aranacak sözcük belirtilerek belirtilen kayıt ve belirtilen kayda yakın kayıtlar getiriliyor. Contains sözcüğü ile birlikte WHERE ile kullanmaya alışık olduğumuz OR ve AND sözcükleri de kullanılabilir. Aşağıdaki sorgu çalıştırıldığında içerisinde &#8220;Aluminum alloy cups&#8221; ya da &#8220;For true&#8221; sözcükleri veya bunlarla benzer söz dizimine sahip olan sözcükleri içeren kayıtlar listeleniyor olacaktır.</p>
<pre class="brush: sql">
SELECT Description
FROM Production.ProductDescription
WHERE CONTAINS ( *, &quot;&quot;Aluminum alloy cups&quot; OR &quot;For true&quot; &quot; )
</pre>
<p><strong>FREETEXT</strong><br />
Freetext sözcüğü ile hedef alan içerisinde belirtilen ifade ve kelimeler çözümlenerek onlara benzer kayıtlar listeleniyor. Bu alanda örnek vermek gerekirse &#8220;Microsoft Türkiye Ofisi&#8221;ndeki seminer oldukça güzeldi ve faydalı bilgiler içeriyordu.&#8221; cümlesini ele alalım. Bu alanda arama yapılırken tanımlanacak olan kelimeler Microsoft, Türkiye, Ofis, Seminer, Güzel, Faydalı, Bilgi şeklinde olacaktır. Tanımlanan ifadeler ise Microsoft Türkiye Ofisi, Faydalı bilgi şeklinde olacaktır. Freetext sözcüğü de aşağıdaki gibi kullanılabilir.</p>
<p><strong>Birkaç basit örnek;</strong></p>
<pre class="brush: sql">
SELECT Description
FROM Production.ProductDescription
WHERE FREETEXT( description,&quot;&quot;For true trail addicts.&quot;&quot;)
</pre>
<p>Kodlara dikkat edilirse bu kodlarda sadece description sütunu üzerinde tanımlı olan index üzerinde arama yapılacaktır.</p>
<pre class="brush: sql">
USE AdventureWorks;
GO
SELECT Title
FROM Production.Document
WHERE FREETEXT (Document, &#039;vital safety components&#039; );
GO

USE Northwind;
GO
SELECT   K.RANK, CompanyName, ContactName, Address
FROM      Customers AS C
         INNER JOIN
         CONTAINSTABLE(Customers,Address, &#039;ISABOUT (&quot;des*&quot;,
            Rue WEIGHT(0.5),
            Bouchers WEIGHT(0.9))&#039;, 3) AS K
         ON C.CustomerID = K.[KEY];
GO

USE AdventureWorks;
GO
DECLARE @SearchWord varchar(30)
SET @SearchWord =&#039;performance&#039;
SELECT Description
FROM Production.ProductDescription
WHERE FREETEXT(Description, @SearchWord);

Use AdventureWorks;
GO
SELECT Name, Color FROM Production.Product
WHERE CONTAINS((Name, Color), &#039;Red&#039;);
</pre>
<p><strong>KONUYLA ALAKALI FAYDALI LİNKLER</strong><br />
<a href="http://msdn.microsoft.com/en-us/library/ms142559.aspx">Querying SQL Server Using Full-Text Search</a><br />
<a href="http://msdn.microsoft.com/en-us/library/cc879245.aspx">Limiting Ranked Result Sets (Full-Text Search)</a><br />
<a href="http://msdn.microsoft.com/en-us/library/ms176078.aspx">FREETEXT (Transact-SQL)</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tugrulhelvaci.com/?feed=rss2&amp;p=558</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Delphi 2010 (Weaver) ve TValue</title>
		<link>http://www.tugrulhelvaci.com/?p=546</link>
		<comments>http://www.tugrulhelvaci.com/?p=546#comments</comments>
		<pubDate>Mon, 10 Aug 2009 18:51:16 +0000</pubDate>
		<dc:creator>sadettinpolat</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[delphi 2010]]></category>
		<category><![CDATA[RTTI]]></category>
		<category><![CDATA[TValue]]></category>
		<category><![CDATA[Weaver]]></category>

		<guid isPermaLink="false">http://www.tugrulhelvaci.com/?p=546</guid>
		<description><![CDATA[Hayatimiz boyunca kim bilir kac defa Showmessage komutunu kullandik ve kim bilir kac defa IntToStr , DateToStr , TimeToStr gibi komutlari kullanarak gostermek istedigimiz degeri string tipine donusturup islemi gerceklestirdik. Bu durumdan kurtulmanin elbet bazi yollari mevcuttu , showmessage komutunu overload etmek , variant tipini kullanmak vs. Bunlardan kimisi  delphi icinde standart olmadigindan astari [...]]]></description>
			<content:encoded><![CDATA[<p>Hayatimiz boyunca kim bilir kac defa Showmessage komutunu kullandik ve kim bilir kac defa IntToStr , DateToStr , TimeToStr gibi komutlari kullanarak gostermek istedigimiz degeri string tipine donusturup islemi gerceklestirdik. Bu durumdan kurtulmanin elbet bazi yollari mevcuttu , showmessage komutunu overload etmek , variant tipini kullanmak vs. Bunlardan kimisi  delphi icinde standart olmadigindan astari yuzunden pahali olan cozumlerdi kimiside yavasliklari nedeniyle tercih edilmiyordu ve bizde en basit islemlerde bile tip cevrimlerini manuel yaparak yolumuza devam etmek zorunda kaliyorduk.</p>
<p><span id="more-546"></span></p>
<p>Overload ifadesi gecmisken asagidaki yaziya da goz atmanizi tavsiye ederim.<br />
<a href="http://tr.delphipeak.com/2007/08/30/overload-metodlar.htm">http://tr.delphipeak.com/2007/08/30/overload-metodlar.htm</a></p>
<p>Delphi 2010 ile birlikte genisletilmis bir RTTI (Run-Time Type Information) destegi bizi bekliyor. Tabi bicok seyde oldugu gibi bu ozelliginde bir iyi tarafi var bir de kotu tarafi. Kotu tarafi kodlarinizin decompile edilmesi eskiye nazaran biraz daha kolay olacak iyi tarafi ise calisma zamaninda nesneler hakkinda artik daha fazla bilgi edinebilecegiz ve bu da bize normalde yapilmasi mumkun olmayan ya da bircok taklalar atmamiza neden olacak isleri artik cok kolay bir sekilde yapabilmemizi saglayacak. Kotu tarafi ile ilgili olarak soyleyebilecegimiz iyi haber ise delphinin bu ozelliginin istege bagli olmasi. Yani istersek bu ozelligi kapatabiliyoruz. </p>
<pre class="brush: delphi">
{$RTTI EXPLICIT METHODS([]) FIELDS([]) PROPERTIES([])}
</pre>
<p>Bu ozellik sayesinde yapabileceklerimiz oldukca genis ama bir iki tane ornek vermek gerekirse en basitindan AOP (Aspect Oriented Programming ) uygulamak isterseniz bu cok basit bir sekilde yapabilirsiniz artik ya da tipini bilmediginiz bir degiskeni bir metoda gonul rahatligi ile gonderebilirsiniz. Tip cevrimleriyle ugrasmak yerine degiskenin tipini delphi calisma zamaninda kendi belirlesin. </p>
<p>Simdi bu degiskenin degerinin calisma zamaninda nasil belirlendigine soyle bir bakalim.</p>
<p>TValue adinda Record olarak tanimlanmis yeni bir yapimiz mevcut Delphi 2010 da. TValue kendisine gonderdigimiz degiskenin tipini calisma zamaninda belirleme gibi ozellikleri icerisinde barindiriyor. </p>
<pre class="brush: delphi">
function TValue.ToString: string;
begin
  if IsEmpty then
    Exit(&#039;(empty)&#039;); // do not localize

  case FData.FTypeInfo^.Kind of
    tkUnknown: Result := &#039;(unknown)&#039;; // do not localize
    tkInteger: Result := IntToStr(AsInt64);
    tkChar: Result := string(AsType);
    tkEnumeration: Result := GetEnumName(FData.FTypeInfo, FData.FAsSLong);
    tkFloat:
      case GetTypeData(FData.FTypeInfo)^.FloatType of
        ftSingle: Result := FloatToStr(FData.FAsSingle);
        ftDouble: Result := FloatToStr(FData.FAsDouble);
        ftExtended: Result := FloatToStr(FData.FAsExtended);
        ftComp: Result := IntToStr(FData.FAsSInt64);
        ftCurr: Result := CurrToStr(FData.FAsCurr);
      end;
    tkString, tkLString, tkUString, tkWString: Result := AsType;
    tkSet: Result := SetToString(FData.FTypeInfo, FData.FAsSLong, True);
    tkClass:
      if FData.FAsObject = nil then
        Result := &#039;(empty)&#039; // do not localize
      else
        Result := Format(&#039;(%s @ %p)&#039;, [AsObject.ClassName, Pointer(AsObject)]);

    tkMethod: Result := Format(&#039;(method code=%p, data=%p)&#039;, [FData.FAsMethod.Code, FData.FAsMethod.Data]); // do not localize
    tkWChar: Result := AsType;
    tkVariant: Result := &#039;(variant)&#039;; // do not localize
    tkArray: Result := &#039;(array)&#039;; // do not localize
    tkRecord: Result := &#039;(record)&#039;; // do not localize
    tkPointer: Result := Format(&#039;(pointer @ %p)&#039;, [Pointer(FData.FAsSLong)]); // do not localize
    tkInterface: Result := Format(&#039;(interface @ %p)&#039;, [Pointer(FData.FHeapData)]); // do not localize
    tkInt64:
      with GetTypeData(FData.FTypeInfo)^ do
        if MinInt64Value &gt; MaxInt64Value then
          Result := UIntToStr(FData.FAsUInt64)
        else
          Result := IntToStr(FData.FAsSInt64);
    tkDynArray: Result := &#039;(dynamic array)&#039;; // do not localize
    tkClassRef:
      if FData.FAsClass = nil then
        Result := &#039;(empty)&#039; // do not localize
      else
        Result := Format(&#039;(class &#039;&#039;%s&#039;&#039; @ %p)&#039;, [FData.FAsClass.ClassName, Pointer(FData.FAsClass)]); // do not localize
  end;
end;</pre>
<p>Eger bir metod TValue tipinde bir parametre aliyorsa biz bu metoda string , integer , obje , tarih gibi bir cok degeri tip cevrimine gerek kalmadan gonderebiliyoruz artik ve gonderdigimiz parametrenin tipini TValue kendisi belirliyor, uygun olan sekilde geriye degerini donderiyor. Kucuk bir ornek yapalim.</p>
<pre class="brush: delphi">
  function DegerYaz(Value: TValue): string;
  begin
    Result := Value.ToString;
  end;</pre>
<p>seklinde bir metodumuz olsun. Aldigi parametreye dikkat ederseniz TValue tipinde. Bu metoda gonderecegimiz her turlu veri tipi gecerlidir. ToString metodu ise bize gonderdigimiz veri tipine uygun olan string ifadeyi tespit edip geri donderecektir.</p>
<pre class="brush: delphi">
program Project2;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Classes,
  Rtti; // --&gt; TValue recordunun tanimlandigi unit.

function DegerYaz(Value: TValue): string;
begin
  result := Value.ToString;
end;

var
  SL: TStringList;
  Obje: TObject;
  Dizi: array of string;
  tarih: TDate;

begin
  try
    tarih := now;
    SL := TStringList.Create;
    SL.Add(&#039;1&#039;);
    SL.Add(&#039;2&#039;);
    SL.Add(&#039;3&#039;);
    Obje := TObject.Create;
    SetLength(Dizi, 2);
    Dizi[0] := &#039;delphi&#039;;
    Dizi[0] := &#039;2010&#039;;
    Dizi[0] := &#039;super&#039;;
    tarih := tarih + 1;
    writeln(DegerYaz(&#039;string ifade&#039;));
    writeln(DegerYaz(1));
    writeln(DegerYaz(True));
    writeln(DegerYaz(SL));
    writeln(DegerYaz(Obje));
    writeln(DegerYaz(tarih));
    readln;
  except
    on E: Exception do
      writeln(E.ClassName, &#039;: &#039;, E.message);
  end;

end.</pre>
<p>bu programin consola ciktisi ise su sekilde olacaktir.</p>
<pre class="brush: delphi">
string ifade
1
True
(TStringList @ 00A4DB00)
(TObject @ 00A30D30)
40036,9031108912)</pre>
<p>Kod yazma aliskanligizi koklu bir sekilde degistirmeye aday olan TValue cok yakinda aramizda olacak <img src='http://www.tugrulhelvaci.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://www.tugrulhelvaci.com/?feed=rss2&amp;p=546</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Derinlemesine Threading..(2)</title>
		<link>http://www.tugrulhelvaci.com/?p=528</link>
		<comments>http://www.tugrulhelvaci.com/?p=528#comments</comments>
		<pubDate>Mon, 20 Jul 2009 02:09:15 +0000</pubDate>
		<dc:creator>Tuğrul HELVACI</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Programlama]]></category>
		<category><![CDATA[Win32]]></category>
		<category><![CDATA[İşletim Sistemi]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[CreateEvent]]></category>
		<category><![CDATA[CreateWaitableTimer]]></category>
		<category><![CDATA[OpenEvent]]></category>
		<category><![CDATA[OpenWaitableTimer]]></category>
		<category><![CDATA[ResetEvent]]></category>
		<category><![CDATA[SetEvent]]></category>
		<category><![CDATA[SetWaitableTimer]]></category>
		<category><![CDATA[Thread]]></category>
		<category><![CDATA[WaitForSingleObject]]></category>

		<guid isPermaLink="false">http://www.tugrulhelvaci.com/?p=528</guid>
		<description><![CDATA[ Bir önceki makalemizde thread&#8217;ler konusuna giriş yapmış, işletim sisteminin thread&#8217;leri nasıl yönettiğini ve thread&#8217;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ı [...]]]></description>
			<content:encoded><![CDATA[<p> Bir <a href="http://www.tugrulhelvaci.com/?p=443">önceki makalemizde</a> thread&#8217;ler konusuna giriş yapmış, işletim sisteminin thread&#8217;leri nasıl yönettiğini ve thread&#8217;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. <span style="color: #ff9900;">Event</span> ve <span style="color: #ff9900;">Waitable Timer</span> 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&#8217;lerimizin belirli kod bloklarında belirli bir şart sağlanana kadar beklemesi için vardırlar.</p>
<p> Hatırlayacağımız üzere, critical section&#8217;lar belirli bir kritik kod bloğuna aynı anda birden fazla thread&#8217;in girmesine müsaade etmiyordu. Mutex&#8217;ler ise critical section&#8217;lara son derece benzemelerine rağmen birden fazla uygulamanın(process) thread&#8217;lerinin de aynı kod bloklarına girişlerini senkronize ediyordu. Ardından temas ettiğimiz semaphore&#8217;ler ise biraz daha farklı bir yaklaşım ile kritik bir kod bloğuna bizim belirlediğimiz sayıda thread&#8217;in girmesini sağlıyordu.</p>
<p> 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.</p>
<p> Event mekanizmaları, birden fazla thread&#8217;in ortaklaşa çalışması söz konusu olduğunda anlamlı olurlar. Bir thread&#8217;in bir diğer thread&#8217;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.<br />
<span id="more-528"></span><br />
 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.</p>
<p> 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&#8217;ın okunması ile tüm ev ahalisi iftarlarını açarlar.</p>
<p> Bu küçük ama ramazan ayında hemen hemen hepimizin yaşadığı senaryonun bilgisayara aktarılması sırasında thread&#8217;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&#8217;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&#8217;ler üstlerine düşen vazifeleri yaparlarken, diğer ev ahalisi hâla sohbet ve televizyon izleme ile iştigal ediyorlar.</p>
<p> İftara 5 dakika kala, çocuk thread&#8217;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.</p>
<p> Senaryomuz hakkında özet bir bilgi verdikten sonra, event senkronizasyon mekanizmasında kullanılan metodların tanımlarına bir bakalım isterseniz;</p>
<pre class="brush: c++">
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
   );
</pre>
<p>ve Delphi tanımlarımız:</p>
<pre class="brush: delphi">
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;
</pre>
<p> 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. <span style="color: #ff9900;">CreateEvent</span> 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, <span style="color: #ff9900;">SetEvent</span> metodu ile açık duruma geçen kapının <span style="color: #ff9900;">ResetEvent</span> 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&#8217;ler kapının önünde beklemeye devam edeceklerdir. Biz örneğimizde bu parametrenin her iki durumunuda kullanacağız.</p>
<p> 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:</p>
<pre class="brush: delphi">
(*
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;
...
...
</pre>
<p> Yukarıdaki kod tanımlarımızda <span style="color: #ff9900;">TAnneThread, TBabaThread, TCocukThread, TMisafirlerThread ve TZamanThread</span> isimli sınıfları görüyorsunuz. Bu sınıflar birbirleri ile etkileşimde olan sınıflardır. Ve herbirisi TZamanThread&#8217;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;</p>
<pre class="brush: delphi">
procedure TForm1.FormCreate(Sender: TObject);
var
  Error : DWord;
begin
  Event_AnneYemekleriIsit := OpenEvent(EVENT_ALL_ACCESS, false, PAnsiChar(&#039;Event Anne Yemekleri Isit&#039;));
  if Event_AnneYemekleriIsit = 0 then
  begin
    Memo1.Lines.Add(&#039;Event bulunamadı, oluşturulacak.&#039;);
    Event_AnneYemekleriIsit := CreateEvent(nil, false, false, PAnsiChar(&#039;Event Anne Yemekleri Isit&#039;));
    Error := GetLastError();

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

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

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

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

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

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

    if Error = ERROR_INVALID_HANDLE then
    	Memo1.Lines.Add(&#039;Event Ezan Okundu ismi daha önce event harici başka bir senkronizasyon nesnesinde kullanılmış.!&#039;);
  end
  else Memo1.Lines.Add(&#039;Event bulundu ve OpenEvent ile açıldı.&#039;);
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 := &#039;Anne: Afiyet olsun.&#039;;

  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 := &#039;Anne: Yemekleri ısıtmaya başladım.:&#039; + 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 := &#039;Baba: Hanım eline sağlık.&#039;;

  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 := &#039;Baba: Bende sofrayı kurmaya başladım:&#039; + TimeToStr(Time);

  Synchronize(Soyle);
end;

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

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

  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 := &#039;Çocuk: Anne ben pide almaya gidiyorum.:&#039; + TimeToStr(Time);

  Synchronize(Soyle);
end;

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

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

  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(&#039;Ezan okundu:&#039; + 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) &gt;= 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) &gt;= fIftarSaati) and (not bPideAlmayaGit) then
    begin
      SetEvent(Event_CocukPideAlmayaGit);
      bPideAlmayaGit := true;
    end;

    if CurrentTime &gt;= 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&#039;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;
</pre>
<p>Yukarıdaki örneğimizde oluşturulan 5 ayrı thread gözlemliyorsunuz. Bu thread&#8217;lerden <span style="color: #ff9900;">TZamanThread</span> herşeyi kontrol eden thread&#8217;imizdir. Diğer thread&#8217;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&#8217;si yardımı ile <em>&#8220;Event_AnneYemekleriIsit&#8221;</em> event değişkenimizi tetiklemiş olur(Kapıyı açar).</p>
<p> Bu tetiklemeye kadar TAnneThread, Execute metodu içindeki <em>WaitForSingleObject(Event_AnneYemekleriIsit, INFINITE);</em> 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 <em>WaitForSingleObject(Event_EzanOkundu, INFINITE);</em> kodu ile ezanın okunmasını beklemeye başlamıştır.</p>
<p> TBabaThread ise Execute metodunda, <em>&#8220;Event_BabaSofrayiKur&#8221;</em> isimli senkronizasyon nesnesini beklemektedir. <span style="color: #ff9900;">TAnneThread.YemekleriIsit</span> metodu içindeki SetEvent ile baba thread&#8217;de beklemeyi bırakmış ve sofrayı kurma görevine başlamıştır. Ardından o da tıpkı anne thread&#8217;de olduğu gibi ezanın okunmasını bekleyecektir.</p>
<p> TZamanThread iftar&#8217;a 5 dakika kala, <em>&#8220;Event_CocukPideAlmayaGit&#8221;</em> 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.</p>
<p> Bu arada TMisafirThread&#8217;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&#8217;ler içinde dahi devam etmelidir <img src='http://www.tugrulhelvaci.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p> Nihayet ezan vakti geldiğinde TZamanThread&#8217;imiz <span style="color: #ff9900;">SetEvent(Event_EzanOkundu);</span> API&#8217;si yardımı ile event nesnesini tetikleyecek ve bu event&#8217;i bekleyen anne, baba, çocuk ve misafir thread&#8217;ler bir sonraki kod bloğuna yani <em>OrucunuAc</em>&#8216;a geçebileceklerdir. Ardından zaman thread&#8217;imiz Terminate kodunu çağırarak TThread sınıfının Terminated property&#8217;sinin true olmasını sağlayacak ve sonsuz döngüden çıkacak, yani threadimiz sonlanacaktır.</p>
<p> Yukarıdaki kod örneğimizde <em>&#8220;Event_EzanOkundu&#8221;</em> olayının CreateEvent ile oluşturulması koduna bir <span style="color: #ff9900;">dikkat !</span> uyarısı yazmıştım. Hatırlarsanız CreateEvent&#8217;in ikinci parametresi bizim event senkronizasyon nesnesinin açık/kapalı olma durumunu otomatik&#8217;mi yoksa manuel&#8217;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&#8217;in olması ve bizim bu thread&#8217;lerin hepsinin birden bekler durumdan kurtulmasını istememizdi.</p>
<p> 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ı <img src='http://www.tugrulhelvaci.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p> 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ı.</p>
<p> Örneğimizi denediğinizde, göreceğiniz çıktı;</p>
<li>Anne: Yemekleri ısıtmaya başladım.</li>
<li>Baba: Bende sofrayı kurmaya başladım.</li>
<li>Çocuk: Anne ben pide almaya gidiyorum.</li>
<li>Ezan okundu.</li>
<li>Baba: Hanım eline sağlık.</li>
<li>Çocuk: Anneciğim ellerine sağlık</li>
<li>Misafir: Yenge ellerine sağlık nefis olmuş</li>
<li>Anne: Afiyet olsun.</li>
<p>gibi olacaktır. Gördüğünüz gibi, Event senkronizasyon mekanizması belirli bir olayı beklemeye dayalıdır. <span style="color: #ff9900;">Olayın gerçeklenme koşulunu sizler yönetirsiniz.</span> Kimi zaman bu bir tuşa basma ile gerçekleşir, kimi zaman bir button yada check box&#8217;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.</p>
<p> Event senkronizasyon mekanizmasını az çok anlattıktan sonra, bir diğer mekanizmamız olan <span style="color: #ff9900;">Waitable Timer</span> kavramına göz gezdirebiliriz. Adından da anlaşılabileceği üzere bu bir timer senkronizasyon nesnedir. Ancak, normal timer&#8217;lar gibi değildir. Normal timer olaylarında, işletim sisteminin bir pencereye ihtiyacı vardır. Bu pencereye belirli periotlarda <span style="color: #ff9900;">WM_TIMER</span> 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&#8217;ları kullanabilirsiniz. Çünkü Waitable Timer&#8217;lar <span style="color: #ff9900;">100 nano saniye</span> hassasiyete sahiptir.</p>
<p> Ö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:</p>
<pre class="brush: c++">
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
   );
</pre>
<pre class="brush: delphi">
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;
</pre>
<p> Görüldüğü gibi bu senkronizasyon mekanizmasının metodlarının biraz daha farklı parametreleri var. <span style="color: #ff9900;">CreateWaitableTimer</span> yada <span style="color: #ff9900;">OpenWaitableTimer</span> metodları diğer senkronizasyon mekanizmalarında olduğu gibi benzerlikler arz ediyor. Lâkin, <span style="color: #ff9900;">SetWaitableTimer</span> şimdiye dek gördüklerimizden farklı bir yapıya sahip. Waitable Timer&#8217;lar tetikleme zamanı için geçeceğiniz zaman bilgisinin <span style="color: #ff9900;">TSystemTime, TFileTime</span> 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;</p>
<pre class="brush: delphi">
  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;
</pre>
<p>Bir Waitable Timer &#8216;a zaman bilgisini geçebilmemiz için öncelikle TSystemTime türündeki record değişkenimizin içerisine gerekli bilgileri yazmalı, ardından <span style="color: #ff9900;">SystemTimeToFileTime</span> API&#8217;si ile gerekli dönüşümü sağlamalı, <span style="color: #ff9900;">LocalFileTimeToFileTime</span> ile bir dönüşüm daha sağladıktan sonra <span style="color: #ff9900;">TLargeInteger</span> türündeki parametremizin yüksek ve düşük word&#8217;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.</p>
<p>SetWaitableTimer&#8217;ın üçüncü parametresi Waitable timer&#8217;ı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&#8217;deki tanım şekli aşağıdaki gibi olacaktır:</p>
<pre class="brush: delphi">
procedure (lpArgToCompletionRoutine: Pointer;
    dwTimerLowValue, dwTimerHighValue: DWORD); stdcall;
</pre>
<p> 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ü <span style="color: #ff9900;">APC(Asynchronous Procedure Call)</span> 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&#8217;sini çağıran thread&#8217;de <span style="color: #ff9900;">SleepEx(INFINITE, true)</span> ile beklemeniz gerektiğini söyleyebilirim. Bu beklemeyi yapmadığınız zaman APC çağrılarını yakalayamazsınız.</p>
<p> Örneğimize geçmeden evvel OpenWaitableTimer için gereken bazı sabitlerin Delphi&#8217;de tanımlı olmadıklarını hatırlatmak ve bu tanımları sizlerle paylaşmak isterim;</p>
<pre class="brush: delphi">
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;
</pre>
<p> 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:</p>
<pre class="brush: delphi">
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(&#039;Waitable Anne Yemekleri Isit&#039;));
  if Waitable_AnneYemekleriIsit = 0 then
  begin
    Memo1.Lines.Add(&#039;Waitable Timer bulunamadı, oluşturulacak.&#039;);
    Waitable_AnneYemekleriIsit := CreateWaitableTimer(nil, false, PAnsiChar(&#039;Waitable Anne Yemekleri Isit&#039;));
    Error := GetLastError();

    if Error = ERROR_INVALID_HANDLE then
    	Memo1.Lines.Add(&#039;Waitable Anne Yemekleri Isit ismi daha önce Waitable Timer harici başka bir senkronizasyon nesnesinde kullanılmış.!&#039;);
  end
  else Memo1.Lines.Add(&#039;Waitable Timer bulundu ve OpenWaitableTimer ile açıldı.&#039;);

  Waitable_CocukPideAlmayaGit := OpenWaitableTimer(TIMER_ALL_ACCESS, false, PAnsiChar(&#039;Waitable Cocuk Pide Almaya Git&#039;));
  if Waitable_CocukPideAlmayaGit = 0 then
  begin
    Memo1.Lines.Add(&#039;Waitable Timer bulunamadı, oluşturulacak.&#039;);
    Waitable_CocukPideAlmayaGit := CreateWaitableTimer(nil, false, PAnsiChar(&#039;Waitable Cocuk Pide Almaya Git&#039;));
    Error := GetLastError();

    if Error = ERROR_INVALID_HANDLE then
    	Memo1.Lines.Add(&#039;Waitable Cocuk Pide Almaya Git ismi daha önce Waitable Timer harici başka bir senkronizasyon nesnesinde kullanılmış.!&#039;);
  end
  else Memo1.Lines.Add(&#039;Waitable Timer bulundu ve OpenWaitableTimer ile açıldı.&#039;);

  Waitable_EzanOkundu := OpenWaitableTimer(TIMER_ALL_ACCESS, false, PAnsiChar(&#039;Waitable Ezan Okundu&#039;));
  if Waitable_EzanOkundu = 0 then
  begin
    Memo1.Lines.Add(&#039;Waitable Timer bulunamadı, oluşturulacak.&#039;);
    Waitable_EzanOkundu := CreateWaitableTimer(nil, true, PAnsiChar(&#039;Waitable Ezan Okundu&#039;)); // İkinci parametreye dikkat, eventlerdeki açıklamamızı hatırlayınız.
    Error := GetLastError();

    if Error = ERROR_INVALID_HANDLE then
    	Memo1.Lines.Add(&#039;Waitable Ezan Okundu ismi daha önce Waitable Timer harici başka bir senkronizasyon nesnesinde kullanılmış.!&#039;);
  end
  else Memo1.Lines.Add(&#039;Waitable Timer bulundu ve OpenWaitableTimer ile açıldı.&#039;);
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 := &#039;Anne: Afiyet olsun.&#039;;

  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 := &#039;Anne: Yemekleri ısıtmaya başladım.:&#039; + TimeToStr(Time);
  Synchronize(Soyle);
end;

{ TBabaThread }

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

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

  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 := &#039;Baba: Bende sofrayı kurmaya başladım:&#039; + TimeToStr(Time);

  Synchronize(Soyle);
end;

{ TCocukThread }

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

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

  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 := &#039;Çocuk: Anne ben pide almaya gidiyorum.:&#039; + TimeToStr(Time);

  Synchronize(Soyle);
end;

{ TMisafirlerThread }

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

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

  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;
</pre>
<p> Evet hepsi bu kadar. Gördüğünüz gibi diğerlerine nazaran tek zorluğu SetWaitableTimer API&#8217;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&#8217;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&#8217;lerimiz çalışmalarına kaldıkları yerden devam edebilme şansını elde edebileceklerdir.</p>
<p> 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&#8217;in <span style="color: #ff9900;">TerminateThread kullanılmadan</span> düzgün bir şekilde nasıl kapatılacağına yönelik olacaktır.</p>
<p>Bir sonraki makalede görüşmek ümidi ile hoşçakalın..</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tugrulhelvaci.com/?feed=rss2&amp;p=528</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Derinlemesine Threading..(1)</title>
		<link>http://www.tugrulhelvaci.com/?p=443</link>
		<comments>http://www.tugrulhelvaci.com/?p=443#comments</comments>
		<pubDate>Sun, 05 Jul 2009 04:19:57 +0000</pubDate>
		<dc:creator>Tuğrul HELVACI</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Win32]]></category>
		<category><![CDATA[İşletim Sistemi]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[CloseHandle]]></category>
		<category><![CDATA[CreateMutex]]></category>
		<category><![CDATA[CreateSemaphore]]></category>
		<category><![CDATA[DeleteCriticalSection]]></category>
		<category><![CDATA[EnterCriticalSection]]></category>
		<category><![CDATA[InitializeCriticalSection]]></category>
		<category><![CDATA[InterlockedCompareExchange]]></category>
		<category><![CDATA[InterlockedDecrement]]></category>
		<category><![CDATA[InterlockedExchange]]></category>
		<category><![CDATA[InterlockedExchangeAdd]]></category>
		<category><![CDATA[InterlockedIncrement]]></category>
		<category><![CDATA[LeaveCriticalSection]]></category>
		<category><![CDATA[OpenMutex]]></category>
		<category><![CDATA[OpenSemaphore]]></category>
		<category><![CDATA[ReleaseMutex]]></category>
		<category><![CDATA[ReleaseSemaphore]]></category>
		<category><![CDATA[Thread]]></category>
		<category><![CDATA[WaitForMultipleObjects]]></category>
		<category><![CDATA[WaitForSingleObject]]></category>

		<guid isPermaLink="false">http://www.tugrulhelvaci.com/?p=443</guid>
		<description><![CDATA[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 [...]]]></description>
			<content:encoded><![CDATA[<p>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&#8217;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.</p>
<p>Thread&#8217;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.</p>
<p><em>İyi bir programcı, üzerinde çalıştığı işletim sistemini tanıyan, onun neleri yapabileceğini ve neleri yapamayacağını bilen programcıdır.</em> 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. &#8220;Çok derin bilgilere girmem, bildiklerim bana yeter, ekmeğimi kazanırım keyfime bakarım&#8221; 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.</p>
<p>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&#8217;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.</p>
<p>Blog&#8217;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&#8217;ye yeni başlayan birisi dahi olsanız, farkında olmadan pointer&#8217;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&#8217;nin kaynak kodlarında dolaşmasını öneriyorum.<br />
<span id="more-443"></span><br />
Evet, low level bilgi sahibi olmanın önemini dilimiz döndüğünce izah etttikten sonra, kaldığımız yerden devam edebiliriz. Threading, korkulan ama bir zaman geldiğinde kaçılamayan bir konudur. Her programcı hayatının bir döneminde bu konu ile yüzleşecektir. Yüzleştiği zaman diliminde alnınızın akı ile sorunları bertaraf edebilmeniz için sizlere yardımcı olmaya çalışacağım.</p>
<p><em>Windows işletim sistemleri; multi-threaded işletim sistemleri değillerdir.</em> Sadece bu hususta taklit yaparlar. İşletim sistemi üzerinde çalışan uygulamalara <span style="color: #ff9900;">process</span> adı verdildiğini biliyorsunuz. Her bir process, en az bir thread&#8217;e sahiptir. Sistem üzerinde çalışan process&#8217;ler Task Manager(Görev Yöneticisi) vasıtası ile gözlemlenebilir. Bu konu ile ilgili yazdığım diğer makaleleri de <a href="http://www.tugrulhelvaci.com/?tag=process">inceleyebilirsiniz</a>.</p>
<p>İşletim sistemimiz içinde çalışan bir çok process ve en az process sayısı kadar thread olduğuna göre, işletim sistemi bu thread&#8217;lerin çalışma mekanizmasını nasıl sağlamaktadır ? İşte threading de en az bilinen ama en önemli hususda bu noktadadır. İşletim sistemi çalışan thread&#8217;leri belirli bir algoritmaya göre sıra ile çalıştırmaktadır. <em>Yani belli bir t zaman diliminde, işletim sistemi üzerinde yalnızca bir tek thread çalışıyor olacaktır.</em></p>
<p>Bu durumda diğer thread&#8217;ler beklemeye alınmış durumdadırlar. Thread işletim sisteminin belirlediği bir zaman dilimi kadar çalıştırıldıktan sonra, sıradaki diğer thread&#8217;e geçilecektir. Bu çizelgeleme işlemine <a href="http://en.wikipedia.org/wiki/Round-robin_scheduling">round robin scheduling</a> adı verilir.Peki işletim sisteminin bu mekanizması nasıldır ? Biraz da bu hususa yakından bakmakta fayda var.</p>
<p>İşletim sistemi thread çalıştırmada zaman paylaşımlı bir yapıya sahiptir. Thread&#8217;leri belirli zaman dilimlerinde çalıştırır ve bir thread&#8217;e ayrılan zaman dolduğunda çalışma sırasındaki diğer thread&#8217;e geçilir. İşte bu thread&#8217;ler arasındaki geçiş zamanına <span style="color: #ff9900;">quanta</span> süresi adı verilir. Ve eğer değişmedi ise Windows işletim sistemlerinde <span style="color: #ff9900;">quanta süresi 20 ms.</span> dir. <strong>Yani bir thread işletim sisteminde en fazla 20 milisaniye çalışabilir.</strong> Bu 20 milisaniyenin dolmasından sonra, sıradaki diğer bir thread çalışacaktır. İşletim sistemi thread&#8217;ler arasında son derece hızla geçerek multi-threaded gibi görünmektedir. Bir thread&#8217;in maksimum çalışabileceği süre anlık olarak 20 ms&#8217;dir ibaresini tekrarlamak istiyorum. <span style="color: #ff9900;">Bunu hiçbir zaman unutmayın</span>, çünkü ilerleyen aşamalarda thread programlamada karşılaştığınız tüm problemlerin ve bu problemlerin çözümüne yönelik teknolojilerin ana sebebinin bu olduğunu anlayacaksınız.</p>
<p>Burada aklımıza bir soru geliyor. İşletim sistemi üzerinde o anda çalışan uygulama sayısının 100 olduğunu varsayalım. Bu durumda en azından 100 çalışan thread&#8217;imiz var demektir. İşletim sistemi thread&#8217;ler arasında 20 ms&#8217;lik zaman dilimlerinde geçiş yapıyorsa eğer, en son thread&#8217;e 2000 ms. sonra yani 2 sn. sonra mı uğrayacaktır ?<br />
Böyle bir durumda uygulamamız 20 ms. çalışıp 2 sn. beklemek zorunda mı kalacaktır ?</p>
<p>Elbette ki hayır. Hernekadar işletim sistemi dediğimiz gibi thread&#8217;ler arasında 20 ms. gibi bir zaman diliminde geçiş yapıyorsa da; bu tarz uzun bekleme problemlerinin engellenmesi adına bir çözüme de sahiptir. Bu çözüme <span style="color: #ff9900;">priority(öncelik)</span> adı verilmiştir. İşletim sisteminin thread&#8217;leri belli zaman dilimlerinde çalıştırması round robin algoritmasına göredir demiştik; işin içine öncelikler girince bu algoritma <span style="color: #ff9900;">priority round robin</span> adını alır.</p>
<p>Bu yöntemde her bir thread belirli bir öncelik seviyesine sahiptir. Öncelik seviyesi yüksek olan thread&#8217;ler işletim sistemince daha ön sıralara alınırlar. Aynı öncelik seviyesine sahip olan thread&#8217;ler öncelik seviyesi yüksek olandan düşük olana doğru sırası ile işletilirler ve öncelik seviyesi yüksek olan grup tamamlandıktan sonra işletim sistemi diğer grubu çalıştırma cihetine gider. Thread öncelikleri <span style="color: #ff9900;">0-31</span> aralığında bir sayısal değer ile ifade edilirler.(<a href="http://msdn.microsoft.com/en-us/library/ms685100(VS.85).aspx">Bknz.</a>)</p>
<p>Bu durumda, thread&#8217;lerin belirli bir öncelik seviyesine göre gruplandığını ve öncelik seviyesi yüksek olanların düşük olanlardan daha önce çalıştırıldığını ve yüksek öncelikli thread&#8217;lerin çalışmasının bitmesine müteakip diğer öncelik gurubuna geçildiğini öğrenmiş bulunuyoruz. Örneğin; işletim sisteminde o anda çalışmakta olan thread&#8217;lerimizin öncelik seviyeleri aşağıda görüldüğü gibi olsun:</p>
<li>Thread-1, Öncelik: 20</li>
<li>Thread-2, Öncelik: 19</li>
<li>Thread-3, Öncelik: 05</li>
<li>Thread-4, Öncelik: 20</li>
<li>Thread-5, Öncelik: 19</li>
<li>Thread-6, Öncelik: 01</li>
<li>Thread-7, Öncelik: 15</li>
<li>Thread-8, Öncelik: 15</li>
<li>Thread-9, Öncelik: 05</li>
<p>Yukarıdaki gibi bir tabloda işletim sistemi öncelikle Thread-1 ve Thread-4 arasında 20 ms.&#8217;lik periyotlar ile geçişler yapacak ve her iki thread&#8217;in de bitmesine müteakip öncelik seviyesi 19 olan Thread-2 ve Thread-5&#8242;e geçecektir. Sırası ile öncelik seviyesi yüksek olan thread&#8217;ler işletilecek ve düşük seviyeli gruba geçiş yapılacaktır.</p>
<p>Yukarıdaki tabloya göre çalıştırılma sırası aşağıdaki gibi olacaktır:</p>
<li>1-Thread-1, Thread-4 (Öncelik 20)</li>
<li>2-Thread-2, Thread-5 (Öncelik 19)</li>
<li>3-Thread-7, Thread-8 (Öncelik 15)</li>
<li>4-Thread-3, Thread-9 (Öncelik 5)</li>
<li>5-Thread-6</li>
<p>Görüldüğü gibi Thread-6 diğer thread&#8217;lere göre çalışmak için çok az zaman bulabilecektir. <em>Ancak işletim sistemi, öncelik seviyesi düşük olan thread&#8217;lerin tamamen ölü görünmemesi adına da bazı mekanizmalara sahiptir.</em> Öncelik sistemi düşük olan thread&#8217;lerin çalışabilmesi için iki koşul vardır.</p>
<p>Birincisi kendilerinden yüksek önceliğe sahip olan tüm thread&#8217;lerin bitmiş olması yada bir nedenden bloke olmuş olmalarıdır. Öncelik seviyesi yüksek olan bir thread <span style="color: #ff9900;">SuspendThread</span> ile duraksatılmış olabilir, yada thread içinde bir deadlock oluşmasından ötürü kararsız duruma gelmiş olabilir. Bu durumda öncelik seviyesi daha düşük olan thread&#8217;ler çalışma fırsatını yakalayabilirler.</p>
<p>İkinci koşulumuz ise; işletim sisteminin düşük öncelikli thread&#8217;lere verdiği bir çalışma şansı ile ilgilidir. Bu şansa <a href="http://msdn.microsoft.com/en-us/library/ms684828(VS.85).aspx">dynamic boosting</a> adı verilir. Eğer bir thread, belirli bir zaman boyunca hiç çalışma şansını elde edememiş ise o zaman işletim sistemi bu thread üzerinde <span style="color: #ff9900;">dynamic boosting</span> işlemi uygular ve thread&#8217;in önceliğini 15 seviyesine yükseltir. Ancak bu yükseltme bir kaç quanta(1 quanta=20 ms.) süresi kadardır. Kendisine işletim sistemi tarafından kıyak geçilen thread, daha sonra tekrar orjinal öncelik seviyesine geri çekilir.</p>
<p>Gördüğünüz gibi, threading&#8217;in arka planında son derece hassas mekanizmalar bulunmaktadır. İşletim sistemi gerçek multi-threaded&#8217;ı sağlamak adına thread&#8217;ler arasında öncelik kavramını yoğun bir şekilde kullanarak aslında bizleri kandırmaktadır <img src='http://www.tugrulhelvaci.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  İşletim sistemi thread&#8217;ler arasında o kadar hızlı geçer ki, biz thread&#8217;lerimizin kesintiye uğradığının farkına dahi varmayız.</p>
<p>Peki, biz bu öncelik seviyelerini nasıl kullanacağız ? Öncelik seviyelerinin ne olduğunu az çok anlattık. Ama makalenin amacı gereği daha derinlere inmemiz gerekiyor <img src='http://www.tugrulhelvaci.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Yazdığımız uygulamalarda öncelik seviyelerini istediğimiz gibi ayarlayabiliriz. Öncelik belirleme işlemimiz, process önceliği ve thread önceliği olarak iki kısımda incelenir. <span style="color: #ff9900;">İşletim sistemi için bir thread&#8217;in önceliği; o thread&#8217;e sahip olan process&#8217;in önceliği ile thread&#8217;in önceliğinin toplamıdır.</span> Öncelik değerlerini <a href="http://msdn.microsoft.com/en-us/library/ms685100(VS.85).aspx">MSDN</a>&#8216;den gözlemleyebilirsiniz.</p>
<p>Bir thread&#8217;in öncelik değerleri Delphi&#8217;de aşağıdaki şekilde tanımlanmıştır:</p>
<pre class="brush: delphi">
  THREAD_BASE_PRIORITY_LOWRT = 15;
  THREAD_BASE_PRIORITY_MAX = 2;
  THREAD_BASE_PRIORITY_MIN = -2;
  THREAD_BASE_PRIORITY_IDLE = -15;

  THREAD_PRIORITY_LOWEST              = THREAD_BASE_PRIORITY_MIN;
  THREAD_PRIORITY_BELOW_NORMAL   = THREAD_PRIORITY_LOWEST + 1;
  THREAD_PRIORITY_NORMAL              = 0;
  THREAD_PRIORITY_HIGHEST             = THREAD_BASE_PRIORITY_MAX;
  THREAD_PRIORITY_ABOVE_NORMAL   = THREAD_PRIORITY_HIGHEST - 1;
  THREAD_PRIORITY_ERROR_RETURN    = MAXLONG;

  THREAD_PRIORITY_TIME_CRITICAL    = THREAD_BASE_PRIORITY_LOWRT;
  THREAD_PRIORITY_IDLE                  = THREAD_BASE_PRIORITY_IDLE;
</pre>
<p>Bu listede özel durumlu iki üye vardır.<span style="color: #ff9900;">THREAD_PRIORITY_IDLE</span> ve <span style="color: #ff9900;">THREAD_PRIORITY_TIME_CRITICAL</span>.</p>
<p>Thread&#8217;ler için bu öncelik seviyelerinin kullanılması; process ve thread öncelik seviye değerlerinin toplanması yerine sabit değere eşitleme yoluna gidilmesine neden olur. THREAD_PRIORITY_IDLE öncelik seviyesinde, işletim sistemi öncelik değerini; thread&#8217;in ait olduğu process&#8217;in öncelik seviyesinin <span style="color: #ff9900;">REALTIME_PRIORITY_CLASS</span> olması durumunda <span style="color: #ff9900;">16</span> diğer durumlarda <span style="color: #ff9900;">1</span> olarak ayarlayacaktır.</p>
<p>THREAD_PRIORITY_TIME_CRITICAL öncelik seviyesinde ise işletim sistemi öncelik değerini; thread&#8217;in ait olduğu process&#8217;in öncelik seviyesinin REALTIME_PRIORITY_CLASS olması durumunda <span style="color: #ff9900;">31</span> diğer durumlarda <span style="color: #ff9900;">15</span> olarak ayarlayacaktır.</p>
<p>Bu karmaşık cümleyi, aşağıda pseudo kod ile göstermeye çalışmak sanırım daha kolay anlaşılmasına vesile olacaktır;</p>
<pre class="brush: delphi">
function GetOSPriority(const Thread : TThread) : Longint;
begin
  if Thread.Priority = THREAD_PRIORITY_IDLE then
    if Thread.Process.Priority = REALTIME_PRIORITY_CLASS
    then Result := 16
    else Result := 1;

  if Thread.Priority = THREAD_PRIORITY_TIME_CRITICAL then
    if Thread.Process.Priority = REALTIME_PRIORITY_CLASS
    then Result := 31
    else Result := 15;
end;
</pre>
<p>Buradan gözlemleyebildiğimiz kadarı ile işletim sisteminin thread&#8217;leri çalıştırması iki ayrı öncelik seviyesine dayanmaktadır. <span style="color: #ff9900;">Process öncelik seviyesi</span> ve <span style="color: #ff9900;">thread öncelik seviyesi</span>. Process&#8217;ler için öncelik seviyeleri aşağıdaki gibidir;</p>
<li> NORMAL_PRIORITY_CLASS</li>
<li> IDLE_PRIORITY_CLASS</li>
<li> HIGH_PRIORITY_CLASS</li>
<li> REALTIME_PRIORITY_CLASS</li>
<p>Yazdığımız uygulamalarda herhangi bir öncelik seviyesi belirtmediğimizde process öncelik seviyesi <span style="color: #ff9900;">NORMAL_PRIORITY_CLASS</span>&#8216;a ayarlanır. Thread&#8217;lerde ise <span style="color: #ff9900;">THREAD_PRIORITY_NORMAL</span> değerine ayarlanmaktadır. Çalışma anında bu değerleri istediğimiz gibi değiştirebiliriz. Bir process&#8217;in öncelik seviyesini <span style="color: #ff9900;">SetPriorityClass</span> API&#8217;si yardımı ile değiştirebileceğimiz gibi, eğer process&#8217;i biz oluşturuyor isek <span style="color: #ff9900;">CreateProcess</span> API&#8217;sine parametre olarak da geçebiliriz. Mevcut bir thread&#8217;in öncelik seviyesini ise <span style="color: #ff9900;">SetThreadPriority</span> API&#8217;sini kullanarak değiştirebileceğimiz gibi, eğer TThread Delphi sınıfını kullanıyorsak bu sınıfın Priority özelliğini değiştirerek de yapabiliriz.</p>
<p>Thread önceliklerinin hayati öneme sahip olduğunu sanıyorum artık anladık. Gerçekten kritik işlemleriniz var ise, process ve thread öncelik seviyelerinizi yüksek değerlerle kullanarak bu işlemlerin diğer thread&#8217;lerden daha önce tamamlanmasını sağlayabilirsiniz. Ancak, kantarın topuzunu fazla da kaçırmamak gerekir bu konularda. Her yazdığınız thread&#8217;i real time thread olarak yazarsanız, işletim sistemini kararsız duruma sokabilir, yada beklenmeyen yavaşlıklara neden olabilirsiniz.</p>
<p>Şu aşamaya geldiğimizde thread&#8217;lerin işletim sistemince nasıl yönetildiğini ve çalışma mekanizmaları hakkında bir nebze de olsa bilgi sahibi olmuş bulunuyoruz. Hâla aklımızda olan ve asla unutmayacağımız 20 ms. lik süreler içinde thread&#8217;ler arasında gezilmesi bize halihazırda ne gibi sorunlar getirir konusuna henüz değinmedik. Biraz da bu konuya değinelim arzu ederseniz.</p>
<p>Daha önce de sıklıkla ifade ettiğimiz gibi; işletim sistemi thread&#8217;ler arasında her 20 ms. de bir geçişler yapıyordu. Şimdi bir senaryo uyduralım. Bu senaryoda, bir thread programımız içinde tanımlı olan <span style="color: #ff9900;">global</span> bir değişkene erişecek ve bu değişkenin değerini kontrol edecek yada değiştirecek.</p>
<p>Bu senaryoda ne gibi bir sorun olabilir sizce ?</p>
<p>Bir sorun olmaz gibi görünüyor, ama eminim sizlerde gerek okuduğunuz bazı kaynaklarda; gerekse de kulaktan dolma bilgilerle thread&#8217;ler içinden dış dünyaya erişimlerde dikkatli olmak gerektiğini biliyorsunuzdur. <strong>Peki neden dikkatli olmamız gerekiyor , neden bir thread içinden dış dünyaya erişmemiz sorunlara sebep oluyor ?</strong> İşte bu soruya yanıt pek bilinmiyor. Maksadımız bu sorunun yanıtını açıklamak ve dolayısı ile thread senkronizasyon mekanizmalarının neden var olduklarını izah etmeye gayret etmek.</p>
<p>Muhtemeldirki thread senkronizasyonlarına yabancı değilsinizdir ve thread&#8217;leriniz içinde kullanmışsınızdır. Ancak bu makaleyi okuduktan sonra bu senkronizasyon mekanizmalarına daha bir farklı gözle bakacağınızdan eminim. En yaygın kullanılanlar, critical sectionlar, muteksler, semaphore&#8217;lar ve eventlerdir. Yine paragrafımızın başında değindiğimiz soruya bir kere daha gönderme yapalım. Bu mekanizmalar niye var, ne gibi sorunlara önlem alıyor bu birbirinden gizemli mekanizmalar ?</p>
<p>Şimdi örneğimize geri dönelim. Bir thread&#8217;imiz olduğunu ve bu thread&#8217;in programımız içinde tanımlı olan bir değişkene ulaşacağını söylemiştik. Bunu kısaca kod ile simüle edelim:</p>
<pre class="brush: delphi">
var
  iValue : Integer = 10;
..
..
..
procedure TMyThread.Execute;
begin
...
...
  if iValue = 10 then iValue := 50;
end;
</pre>
<p>Bu son derece masumane kod, bir değişkenin değerinin 10 olup olmadığını kontrol ediyor ve değer 10 ise 50 ataması yapıyor. Aslında burada son derece ciddi problemler olabilir. Burada bakış açımızı biraz daha derinlere çevirmemiz , makina dili seviyesinde düşünmemiz gerekiyor. Yazdığımız tüm kodların makina diline çevrildiğini biliyorsunuz. Masum bir değişkene değer atamanın yada o değişkendeki değeri kontrol etme kodunun dahi makina dilinde bir karşılığı vardır. Bu karşılıkları Delphi&#8217;de <span style="color: #ff9900;">CPU Window</span>&#8216;u açarak görebilirsiniz.</p>
<p>Bir değişkenin değerinin kontrol edilmesi yada o değişkene değer atanması işlemi makina dilinde birden fazla satıra karşılık geliyor olabilir. Diyelim ki bir değişkenin değerinin kontrol edilmesi, makina dilinde(assembler) 4 satırdan oluşsun ve bu değişkene değer atanması da 6 satırdan oluşsun. Böyle bir durumda işletim sistemi tarafından çalıştırılan thread tam değişkenin değerini kontrol ederken, yani &#8220;if Value = 10&#8243; işlemini yaparken makina dili kodlarının 3ncü satırında beklemeye alınırsa(20 ms&#8217;lik işletme zamanı dolarsa) ne olur ?</p>
<p>Söylemimizi daha anlaşılır kılmak adına aşağıdaki pseudo koda bakabilirsiniz:</p>
<pre class="brush: delphi">
//if iValue = 10
asm
  mov ..
  pop ..
  push.. // Thread 20 ms.&#039;lik çalışma süresini bu noktada bitirir ise !!
  je  ..
end;

// iValue := 50;
asm
  mov ..
  mov ..
  add ..
  pop ..
  push..
  nop ..
end;
</pre>
<p>Yukarıdaki kodlar tamamen uydurma kodlardır. Amacımız ne demek istediğimizi bu uydurma kodlar ile daha anlaşılır hâle getirmeye çalışmaktır.</p>
<p>Bu durumda geriye kalan 1 satırlık kod henüz işletilmemiş durumdadır. Ve aynı sınıfın bir başka thread&#8217;i bu değişkene erişip değişkenin değerini kendi 20 ms. lik zaman diliminde değiştirirse birinci thread hatalı çalışabilir. Bu ve benzeri senaryolar, birden fazla thread&#8217;in ortak kaynakları kullandığı her ortamda gerçekleşebilir. İşte bu sebeplerden ötürü, thread&#8217;lerde senkronizasyonlar son derece önemlidir. Kritik kod bloklarının birden fazla thread&#8217;in erişimi sırasında güvenli duruma getirilmesi gerekir. Buna genellikle <span style="color: #ff9900;">atomik erişim</span> adı verilir. <em>İşletim sistemi thread&#8217;ler arasındaki zaman paylaşım mekanizmasını kendisi yürütür ve bu mekanizmaya direkt erişime müsaade etmemiştir.</em> Eğer biz işletim sistemine &#8220;thread&#8217;ler arasında geçiş yapma&#8221; diyebilse idik, thread senkronizasyonlarına hiç gerek kalmazdı. Ancak bu derece ciddi bir husus programcıların insiyatifine bırakılmayacak kadar önemli olduğu için bu mekanizma işletim sistemi tarafından yönetilir.</p>
<p>Bir veri kaynağına multi-threaded ortamlarda güvenli(atomik) erişmek için işletim sistemi bize pek çok senkronizasyon fonksiyonu sağlamıştır. Bunlardan <span style="color: #ff9900;">InterlockedIncrement, InterlockedCompareExchange, InterlockedDecrement, InterlockedExchange ve InterlockedExchangeAdd</span> metodları thread&#8217;lerin global değişkenlere erişimlerini güvenli hale getiren metodlardır. Bu metodların tanım şekilleri aşağıdaki gibidir:</p>
<pre class="brush: c++">
LONG InterlockedIncrement(
  LPLONG lpAddend
);

PVOID InterlockedCompareExchange(
  PVOID *Destination,
  PVOID Exchange,
  PVOID Comperand
);

LONG InterlockedDecrement(
  LPLONG lpAddend
);

LONG InterlockedExchange(
  LPLONG Target,
  LONG Value
);

LONG InterlockedExchangeAdd (
  PLONG Addend,
  LONG Increment
);
</pre>
<p>ve Delphi tanımları:</p>
<pre class="brush: delphi">
  function InterlockedIncrement(var Addend: Integer): Integer; stdcall;
  function InterlockedDecrement(var Addend: Integer): Integer; stdcall;
  function InterlockedExchange(var Target: Integer; Value: Integer): Integer; stdcall;
  function InterlockedCompareExchange(var Destination: Pointer; Exchange: Pointer; Comperand: Pointer): Pointer stdcall;
  function InterlockedExchangeAdd(var Addend: Longint; Value: Longint): Longint stdcall; overload;
</pre>
<p>Bu bilgiler ışığında yukarıdaki örneğimizi yeniden yazarsak;</p>
<pre class="brush: delphi">
var
  iValue : Integer = 10;
..
..
..
procedure TMyThread.Execute;
begin
...
...
  if InterlockedCompareExchange(Pointer(iValue), Pointer(iValue), Pointer(0)) = Pointer(10)
  then InterlockedExchange(iValue, 50);
end;
</pre>
<p>Gördüldüğü üzere birden fazla thread&#8217;in erişebileceği bir değişkenin değerinin kontrolü sırasında InterlockedCompareExchange metodunu, ve bu değişkene değer atarken de InterlockedExchange metodunu kullandık. Bu metodlar ilgili değişkene güvenli bir şekilde erişmemizi ve veriyi düzgün bir şekilde değiştirmemizi sağlarlar. <em>Bu metodların kullanılması thread&#8217;in 20 ms. lik çalışma süresinin işletim sistemi tarafında sonuna da gelinmiş olsa, işlem bitmeden bir başka thread&#8217;e geçilmeyeceğinin garantisini verir.</em> En önemli özelliklerinin özetlenmesi gerekir ise; işletim sistemi bu metodlar kullanımda iken thread&#8217;ler arası geçişi durdurur. Kısacası, bir global değişkenin değerinin değiştirilmesi yada kontrol edilmesi sırasında oluşabilme ihtimali olan thread geçişlerinin önüne set olur ve işleminiz hatasız bir şekilde sonlanır.</p>
<p>InterlockedCompareExchange ilk değer olarak içeriği kontrol edilecek yada değişime uğrayacak değişkenimizin adresini alır. İkinci parametresi ise; kontrol edilecek değerin eşit olması durumunda değiştirilecek bilgiyi içerir. Üçüncü parametre, global değişkenimizin kontrol edileceği değerin adresidir. Global değişkenimiz üçüncü parametredeki değer ile eşit olduğu durumda, 2nci parametrede belirtilen değere set edilir. Fonksiyonun geri dönüş değeri ise InterlockedCompareExchange&#8217;den önce global değişkenin<br />
sahip olduğu değerdir. <span style="color: #ff9900;">Bu metodun en can alıcı noktası kendisine 32 bit veri türlerini kabul ediyor olmasıdır.</span> İşletim sistemi bu fonksiyona parametre olarak geçilen değerleri kendi içinde 32 bit bir kıyaslamaya tabii tutacaktır.</p>
<p>Bu durumda InterlockedCompareExchange fonksiyonuna sadece 32 bit veri türlerinin geçilmesi gerekir. Bilindiği gibi, işletim sistemi 32 bit olduğu için Pointer türü de 32 bit&#8217;dir. Böyle bir durum söz konusu iken 32 bit&#8217;ten daha büyük değerlerin bu fonksiyon ile kontrol edilmesi mümkün değildir.Eğer 64 bit global değişkenleriniz var ise ve bu değişkenleri atomik düzeyde kontrol etmek istiyor iseniz, uygulamanızın çalıştığı işletim sisteminin minimum Windows Vista olması gerekir. Windows Vista altında <a href="http://msdn.microsoft.com/en-us/library/ms683562(VS.85).aspx">InterlockedCompareExchange64</a> isimli fonksiyonu 64 bit veri kıyaslaması için kullanabilirsiniz. </p>
<p>Tüm bu açıklamalar bizde Delphi&#8217;deki 32 bit harici veri türlerinin atomik olarak nasıl kontrol edileceği hususunda bazı soru işaretleri oluşmasına neden olur. Bu veri türlerinden Boolean en çok kullanılan veri türü sayılabilir. Bir boolean veri türünü global olarak tanımlarsak ve bunu atomik düzeyde InterlockedCompareExchange ile kontrol etmek ister isek ne yapacağız ?</p>
<p>Bildiğiniz gibi Boolean veri türü, 1 Byte&#8217;lık(8 bit) yer kaplamaktadır. Oysa fonksiyonumuz 32 bit veri türleri ile çalışır. Bu durumda; Delphi&#8217;nin Boolean tipi yerine Win32&#8242;deki <span style="color: #ff9900;">LongBool</span> veri tipini kullanabilirsiniz. Örneğin;</p>
<pre class="brush: delphi">
uses
  StrUtils; // IfThen fonksiyonu için..
..
..
var
  GlobalBool : LongBool = false;
..
..
var
  pResult : Pointer;
begin
  pResult := InterlockedCompareExchange(Pointer(GlobalBool), Pointer(LongBool(true)), Pointer(LongBool(false)));

  if pResult = Pointer(LongBool(true))
  then ShowMessage(&#039;Global Boolean değişkenimiz InterlockedCompareExchange den önce True idi&#039;)
  else  ShowMessage(&#039;Global Boolean değişkenimiz InterlockedCompareExchange den önce false idi&#039;);

  ShowMessage(&#039;GlobalBool un yeni değeri:&#039; + IfThen(GlobalBoolean, &#039;Yeni değer True&#039;, &#039;Yeni değer False&#039;));
end;
</pre>
<p>Yukarıdaki kod örneği; global bir sahada tanımlanmış LongBool türündeki değişkenimizin değerini atomik seviyede kontrol eder ve gerekir ise değiştirir. InterlockedCompareExchange metodunun birinci parametresi global boolean değişkenimizi gösterir. İkinci parametremiz bu değişkenin almasını istediğimiz yeni değeri ifade eder. Üçüncü parametremiz ise global değişkenimizin karşılaştırılacağı değerdir. Fonksiyondan geriye dönen değer, InterlockedCompareExchange fonksiyonunun çağırılmasından evvel global değişkenin sahip olduğu değerdir.</p>
<p>Bu mekanizma işletim sistemindeki thread senkronizasyonlarından sadece bir tanesidir. Eğer uygulamanız içinde kullandığınız thread&#8217;lerin global değişkenlere erişimi söz konusu ise, bu metodları kullanmak sizlere güvenli bir kodlamanın yollarını açacaktır.</p>
<p><span style="color: #ffcc00;"><strong>Critical Section</strong></span></p>
<p>Bir diğer popüler senkronizasyon mekanizması da; critical section&#8217;lardır. Critical Section&#8217;lar en bilinen thread senkronizasyon mekanizmalarından bir tanesidirler. Burada amaç; bir thread&#8217;in belirli bir kritik kodu çalıştırırken , başka bir thread&#8217;in aynı kod bloğuna girmesine mani olmaktır. Bu mekanizmanın en sık kullanılan metodları; <span style="color: #ff9900;">InitializeCriticalSection, EnterCriticalSection, LeaveCriticalSection ve DeleteCriticalSection</span>&#8216;dır.</p>
<p>C++ ve Delphi tanımları aşağıdaki gibidir:</p>
<pre class="brush: c++">
VOID InitializeCriticalSection(
  LPCRITICAL_SECTION lpCriticalSection
);

VOID EnterCriticalSection(
  LPCRITICAL_SECTION lpCriticalSection
);

VOID LeaveCriticalSection(
  LPCRITICAL_SECTION lpCriticalSection
);

VOID DeleteCriticalSection(
  LPCRITICAL_SECTION lpCriticalSection
);
</pre>
<pre class="brush: delphi">
_RTL_CRITICAL_SECTION = record
  DebugInfo: PRTLCriticalSectionDebug;
  LockCount: Longint;
  RecursionCount: Longint;
  OwningThread: THandle;
  LockSemaphore: THandle;
  Reserved: DWORD;
end;

  TRTLCriticalSection = _RTL_CRITICAL_SECTION;

  procedure InitializeCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;
  procedure EnterCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;
  procedure LeaveCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;
  procedure DeleteCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;
</pre>
<p>Critical Section&#8217;lar thread programlamada sıklıkla kullanılan ama <span style="color: #ff9900;">aynı zamanda sıklıkla yanlış kullanılan mekanizmalardandır.</span> Kullanımında son derece dikkatli olmamız gereken bir nokta vardır. Ancak o noktaya değinmeden evvel isterseniz critical section kullanımını hayalimizde canlandırmaya çalışalım.</p>
<p>Şimdi elektrik faturası ödemek üzere evinize en yakın elektrik idaresine gittiğinizi düşünün. Elektrik idaresinde faturanızı yatırabilmeniz maksadı ile 10 memur çalışıyor olsun.<br />
Her bir memurun kendisine ait de bir odası olsun. Bu odalara 1&#8242;den 10&#8242;a kadar numara verdiğimizi düşünelim. Memurlar, bir seferde sadece bir aboneyi odaya kabul etsinler ve bir kişinin faturası ile ilgilensinler. Bir abone faturasını yatırmak için memurun bulunduğu odaya girdiğinde oda kapısının müstahdem(hademe) tarafından kapatıldığını ve başkalarının odaya girmesine müstahdemin izin vermediğini düşünelim. Böyle bir sistemde bir oda içinde aynı anda birden fazla elektrik abonesi bulunamaz. Bir abonenin içeride olduğunu bilen müstahdem kapıdan içeri girmeye çalışacak olan bir diğer aboneyi beklemesi hususunda uyaracaktır.</p>
<p>Bu örneğimizde oda; güvenle çalışmasını istediğimiz koddur. Müstahdem işletim sistemidir. Oda kapısı ise critical section&#8217;dır. Tıpkı bu örnekte olduğu gibi, critical section&#8217;lar bir flag gibi çalışır. Açık yada kapalı bayrakları vardır. Bir oda kapısı ya açıktır yada kapalıdır mantığı critical section&#8217;lar içinde geçerlidir. Oda kapısının açık/kapalı olma durumuna göre odaya yeni bir abone alımından sorumlu olan müstahdem programlama dünyasında işletim sistemidir. İşletim sistemi, bir critical section ile işaretlenmiş olan kod bloğuna müstahdemlik yaparak koruma sağlayacak ve aynı kod bloğuna başka thread&#8217;lerin girmesine izin vermeyecektir.</p>
<p>EnterCriticalSection kapının kapandığı durumdur. Kapı bir kere kapandıktan sonra ancak LeaveCriticalSection ile açık duruma geçebilir. Daha teknik tabirler ile ifade etmek icap ederse; EnterCriticalSection işletim sistemi içerisinde bir değişkenin değerini set eder. Bir başka thread EnterCriticalSection kod bloğuna geldiğinde bu değişkenin değerinin ne olduğuna bakar. Eğer set edilmiş ise(yani kapı kapalı ise) o zaman beklemeye başlar. LeaveCriticalSection ise, set edilmiş olan işletim sistemine has değişkeni tekrar eski haline alır. Bu durumda beklemekte olan diğer thread zaman kaybetmeden içeri girer ve sistem bu şekilde işlemeye devam eder.</p>
<p>Delphi&#8217;de yukarıda tanımlarını verdiğimiz critical section metodlarını kullanmak için <span style="color: #ff9900;">TRTLCriticalSection</span> türünden bir yapıya ihtiyaç duyuyorduk. İşte bu yapının tanımlandığı yer ile ilgili <span style="color: #ff9900;">programcılar büyük hatalara düşmektedirler.</span> Bu hataların neler olduğunu örnek bir kod ile göstermek isterim:</p>
<pre class="brush: delphi">
TMyThread = class(TThread)
private
  Section 		: TRTLCriticalSection;

  fName,
  fStartValue,
  fStopValue	: String;
protected
  procedure Execute; override;
public
  constructor Create(const AName : String);
  destructor Destroy; override;

  property StartValue : String read fStartValue;
  property StopValue 	: String read fStopValue;
  property Name		: String read fName;
end;

implementation

{$R *.dfm}

{ TMyThread }

constructor TMyThread.Create(const AName : String);
begin
  inherited Create(true);
  FreeOnTerminate := true;
  fName := AName;

  InitializeCriticalSection(Section);

  Resume;
end;

destructor TMyThread.Destroy;
begin
  DeleteCriticalSection(Section);
  form1.Memo1.Lines.Add(&#039;Name:&#039; + Name + &#039; Start Time:&#039; + StartValue + &#039; Stop Time:&#039; + StopValue);

  inherited;
end;

procedure TMyThread.Execute;
var
  iCounter : Integer;
begin
  inherited;

  EnterCriticalSection(Section);
  fStartValue := TimeToStr(Time);

  Sleep(10000);

  fStopValue  := TimeToStr(Time);
  LeaveCriticalSection(Section);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  mThread1,
  mThread2 : TMyThread;
begin
  mThread1 := TMyThread.Create(&#039;Birinci Thread&#039;);
  Sleep(2000);
  mThread2 := TMyThread.Create(&#039;İkinci Thread&#039;);
end;
</pre>
<p>Yukarıdaki kod örneğinde, Section isminde bir TRTLCriticalSection değişkenimiz var ve bu değişken TMyThread sınıfının bir üyesi olarak tanımlanmış. TMyThread&#8217;in Execute kodunda critical section&#8217;a girdiğimizi EnterCriticalSection metodu vasıtası ile söylemiş oluyoruz. Hemen ardından TimeToStr ile o anki zaman bilgisini bir değişkende depoluyoruz. Ardından 10 saniye bekliyoruz ve yeni zaman bilgisini bir başka değişkende depoluyoruz. Nihayet, LeaveCriticalSection metodunu çağırıyoruz ve kritik kod bloğundan çıktığımızı söylüyoruz.</p>
<p>Şimdi dikkatinizi Button&#8217;un altındaki koda verin. Burada aynı thread sınıfından iki değişken tanımlanmış ve birincisi oluşturulup otomatikman çalıştırılır çalıştırılmaz(Resume çağrısı bir thread&#8217;i başlatır), 2 saniyelik bir bekleme yapılmış ve ardından diğer thread oluşturulmuş ve çalıştırılmıştır.</p>
<p>Burada beklentilerimizin ne olduğu öncelikle bir yazalım ardından sonucu birlikte irdeleyelim.</p>
<p><strong>Beklentimiz:</strong></p>
<li>Birinci Thread: Çalışma başlama zamanının 02:40:00 olduğu varsayılırsa, çalışma bitiş zamanı 02:40:10 olmalıdır.</li>
<li>İkinci Thread : Çalışma başlama zamanı 02:40:10 ve çalışma bitiş zamanı da 02:40:20 olmalıdır.</li>
<p>Critical section&#8217;ların çalışma mekanizmalarını tekrar hatırlayacak olursanız, bir thread&#8217;in kritik kod bloğuna girmesi bir başka threadin orada beklemesine neden oluyordu. Bizim örneğimizde &#8220;Birinci Thread&#8221; koda 02:40:00 &#8216;da girdiğinde &#8220;İkinci Thread&#8221; aynı kod bloğuna 02:42:00 &#8216;da girmeye çalışacak ancak orada bir başka thread olduğu için giremeyecektir. &#8220;İkinci Thread&#8221; imiz ancak 02:40:10&#8242;da içeriye girecektir.</p>
<p>Critical Section&#8217;ların doğası gereği olması gereken budur. Ancak bu örnekte beklediğimizi bulamayacağız. Çalıştırıp sonuçlarına bir bakalım:</p>
<p><strong>Sonuç</strong></p>
<li>Name:Birinci Thread Start Time:02:45:52 Stop Time:02:46:02</li>
<li>Name:İkinci Thread  Start Time:02:45:54 Stop Time:02:46:04</li>
<p>Sizinde gördüğünüz gibi &#8220;İkinci Thread&#8221; birincisini beklemeden içeriye girebilmiştir. Peki bu nasıl oldu ? Onca yazdığımız şey doğru değilmiydi ?</p>
<p>Elbetteki doğru idi. Ancak burada programcıların %95&#8242;inin yaptığı bir hatayı simüle ettik. TRTLCriticalSection türündeki ve &#8220;Section&#8221; ismindeki değişken <span style="color: #ff9900;">TMyThread&#8217;in bir üye değişkenidir.</span> Ve bu üye değişkene TMyThread sınıfı oluşturulduğunda hafızada bir yer ayırılmaktadır. TMyThread sınıfının oluşturulan her bir instance&#8217;ı için <em>Section değişkeninin hafızadaki adresi farklı bir yeri gösterecektir.</em></p>
<p>Tekrar elektrik idaresi örneğine dönecek olursak; &#8220;Birinci Thread&#8221; için Section 5 numaralı odayı, &#8220;İkinci Thread&#8221; için ise 10 numaralı odayı gösteriyor olabilir. Böylece; &#8220;Birinci Thread&#8221; 5 numaralı odanın kapısını açmış ve içeri girmiş; &#8220;İkinci Thread&#8221; ise 10 numaralı odanın içinde kimse olmadığı için o da 10 numaralı odaya girebilmiştir.</p>
<p>Buradan anlaşılması gereken özet bilgi şudur: critical section&#8217;ları işaret eden değişkeni bir thread&#8217;in yerel değişkeni yaparsanız, hiç bir zaman istediğinizi elde edemezsiniz. Bu yerel değişken sadece ve sadece o anda create edilmiş olan instance için anlamlıdır. Diğer thread instance&#8217;larını bağlamaz. Şimdi bu Section değişkenini global bir alana taşıyıp kodumuzu revize edelim ve test sonuçlarını burada paylaşalım:</p>
<pre class="brush: delphi">
TMyThread = class(TThread)
private
  fName,
  fStartValue,
  fStopValue	: String;
protected
  procedure Execute; override;
public
  constructor Create(const AName : String);
  destructor Destroy; override;

  property StartValue : String read fStartValue;
  property StopValue 	: String read fStopValue;
  property Name		: String read fName;
end;

var
  Form1: TForm1;
  Section	: TRTLCriticalSection;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  mThread1,
  mThread2 : TMyThread;
begin
  mThread1 := TMyThread.Create(&#039;Birinci Thread&#039;);
  Sleep(2000);
  mThread2 := TMyThread.Create(&#039;İkinci Thread&#039;);
end;

{ TMyThread }

constructor TMyThread.Create(const AName : String);
begin
  inherited Create(true);
  FreeOnTerminate := true;
  fName := AName;

  Resume;
end;

destructor TMyThread.Destroy;
begin
  form1.Memo1.Lines.Add(&#039;Name:&#039; + Name + &#039; Start Time:&#039; + StartValue + &#039; Stop Time:&#039; + StopValue);

  inherited;
end;

procedure TMyThread.Execute;
var
  iCounter : Integer;
begin
  inherited;

  EnterCriticalSection(Section);
  fStartValue := TimeToStr(Time);

  Sleep(10000);

  fStopValue  := TimeToStr(Time);
  LeaveCriticalSection(Section);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  InitializeCriticalSection(Section);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  DeleteCriticalSection(Section);
end;
</pre>
<p>Kodumuzun bu yeni halinde, TMyThread sınıfının constructor&#8217;ındaki InitializeCriticalSection kaldırılmış ve formumuzun OnCreate event&#8217;ine taşınmıştır. Benzer bir şekilde, destructor&#8217;daki DeleteCriticalSection metodu formumuzun OnDestroy metoduna taşınmış ve TMyThread içinde TRTLCriticalSection türündeki değişkenimiz global alana taşınmıştır. Geri kalan kodlar bire bir aynıdır. Buyrun çalıştırma sonuçlarına birlikte bakalım:</p>
<li>Name:Birinci Thread Start Time:02:54:01 Stop Time:02:54:11</li>
<li>Name:İkinci  Thread Start Time:02:54:11 Stop Time:02:54:21</li>
<p>Görüldüğü üzere &#8220;İkinci Thread&#8221; &#8216;imiz &#8220;Birinci Thread&#8221; işini bitirmeden kod bloğundan içeri girememiştir. Yani critical section&#8217;lardan beklenen davranış oluşmuştur. Bu husus son derece önemlidir ve pek çok programcının yaptığı ciddi thread hatalarının başında gelir. Critical section&#8217;ları Windows API&#8217;leri ile kullanmak yerine kullanımı daha kolay olan ama bire bir aynı işi yapan <span style="color: #ff9900;">TCriticalSection</span> Delphi sınıfı ile kullanabilirsiniz. TCriticalSection sınıfı <span style="color: #ff9900;">SyncObjs.pas</span> isimli dosyada tanımlanmıştır.<span style="color: #ff9900;">Enter ve Leave</span> isimli iki metoda sahiptir. Tahmin edebileceğiniz gibi, yada Delphi kaynak kodlarından gözlemleyebileceğiniz gibi bu metodlar içlerinde EnterCriticalSection ve LeaveCriticalSection metodlarını çağırmaktadırlar.</p>
<p>Critical Section&#8217;lar hakkında son söz olarak aşırı kullanımının performansa olumsuz etkileri olacağını belirtmek isterim. Bu sebeple, thread içindeki kodlarınızı optimize etmeli ve sadece gereken noktalarda critical section&#8217;ları kullanmalısınız. Critical Section içindeki kod bloklarınızı mümkün mertebe kısa tutmalısınız.</p>
<p><span style="color: #ffcc00;"><strong>WaitForSingleObject &#038; WaitForMultipleObjects</strong></span></p>
<p>Bir sonraki senkronizasyon mekanizmamız olan mutex&#8217;ler kavramını izaha başlamadan evvel ihtiyacımız olan iki API fonksiyonuna değinmemiz gerekiyor. Bunlar <span style="color: #ff9900;">WaitForSingleObject</span> ve <span style="color: #ff9900;">WaitForMultipleObjects</span> API&#8217;leri.</p>
<p>Bu iki API fonksiyonu senkronizasyon mekanizmaları için son derece önemlidir. Bu fonksiyonlar beklemek amacı ile kullanılırlar. İşletim sistemindeki senkronizasyon mekanizmaları bir önceki örneğimizde bahsettiğimiz critical section&#8217;larda da olduğu gibi bir kapı şeklinde çalışırlar. Kapı ya açıktır yada kapalıdır. WaitForSingleObject ve WaitForMultipleObjects fonksiyonları kapı kapalı olduğu müddetçe beklemek amacı ile vardır.</p>
<p>Öncelikle tanımlarına yakından bir bakalım:</p>
<pre class="brush: c++">
DWORD WaitForSingleObject(
  HANDLE hHandle,
  DWORD dwMilliseconds
);

DWORD WaitForMultipleObjects(
  DWORD nCount,
  CONST HANDLE *lpHandles,
  BOOL bWaitAll,
  DWORD dwMilliseconds
);
</pre>
<p>ve Delphi tanımları aşağıdaki gibidir:</p>
<pre class="brush: delphi">
  MAXIMUM_WAIT_OBJECTS = 64;

  TWOHandleArray = array[0..MAXIMUM_WAIT_OBJECTS - 1] of THandle;
  PWOHandleArray = ^TWOHandleArray;

  function WaitForSingleObject(hHandle: THandle; dwMilliseconds: DWORD): DWORD; stdcall;

  function WaitForMultipleObjects(nCount: DWORD; lpHandles: PWOHandleArray;
  bWaitAll: BOOL; dwMilliseconds: DWORD): DWORD; stdcall;
</pre>
<p>Her iki fonksiyonda bekleme yapacakları nesnelerin tutacaklarını(<span style="color: #ff9900;">THandle</span>) parametre olarak almaktadırlar. Bu fonksiyonlarla; <span style="color: #ff9900;">CreateEvent</span> yada <span style="color: #ff9900;">OpenEvent</span>, <span style="color: #ff9900;">CreateMutex</span> yada <span style="color: #ff9900;">OpenMutex</span>, <span style="color: #ff9900;">CreateProcess</span> yada <span style="color: #ff9900;">OpenProcess</span>, <span style="color: #ff9900;">CreateSemaphore</span> yada <span style="color: #ff9900;">OpenSemaphore</span>, <span style="color: #ff9900;">CreateThread</span> yada <span style="color: #ff9900;">CreateRemoteThread</span>, <span style="color: #ff9900;">CreateWaitableTimer</span> yada <span style="color: #ff9900;">OpenWaitableTimer</span> fonksiyon çağırımlarından geri dönen handle değerlerini bekleyebilirsiniz.</p>
<p>Yani, WaitForSingleObject yada WaitForMultipleObjects ile; bir Event&#8217;in, bir Mutex&#8217;in, bir Process&#8217;in, bir Semaphore&#8217;un, bir Thread&#8217;in yada bir Waitable Timer&#8217;ın bitmesini bekleyebilirsiniz. WaitForSingleObject&#8217;in ikinci parametresi, milisaniye cinsinden bekleme zaman bilgisidir. Tanımından da görebileceğiniz gibi, bu zaman bilgisi DWord türündedir ve DWord 32 bit bir tamsayıdır. 32 bit bir tamsayının alabileceği maksimum değer, <em>High(DWord) = $FFFF FFFF = 4.294.967.295 ms. = 49.7</em> gündür. Tam bu noktada 49.7 gün ile ilgili bir diğer <a href="http://www.tugrulhelvaci.com/?p=326">makalemizi</a> incelemeniz faydalı olabilir.</p>
<p>Delphi&#8217;de <span style="color: #ff9900;">INFINITE</span> isimli sabit aşağıdaki şekilde tanımlanmıştır:</p>
<pre class="brush: delphi">
  INFINITE = DWORD($FFFFFFFF);
</pre>
<p>WaitForSingleObject fonksiyonunun ikinci parametresine genellikle INFINITE sabitini geçeriz. Bu da fonksiyonumuzun beklediği işletim sistemi nesnesini 49.7 gün boyunca beklemesi anlamına gelir. Hemen kısa bir örnek vererek yazdıklarımızı zihninizde pekiştirelim;</p>
<p>Örneğimizde bir thread oluşturacak ve bu thread&#8217;in sonlanmasını bekleyeceğiz;</p>
<pre class="brush: delphi">
TMyThread = class(TThread)
protected
  procedure Execute; override;
public
  constructor Create;
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

{ TMyThread }

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

  Resume;
end;

procedure TMyThread.Execute;
begin
  inherited;

  Sleep(20000);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  mThread : TMyThread;
  RetVal    : DWord;
begin
  mThread := TMyThread.Create;
  RetVal := WaitForSingleObject(mThread.Handle, INFINITE);
  ShowMessage(&#039;Thread bitti&#039;);
end;
</pre>
<p>Yukarıdaki örneğimizde basit bir thread sınıfımız olduğunu görüyorsunuz. Thread&#8217;in çalışma kodunda hiç bir şey yapmadan 20 saniye bekliyoruz. Thread&#8217;i oluşturan Button1&#8242;in OnClick metodunda ise WaitForSingleObject ile thread&#8217;in bitmesini bekliyoruz. Thread&#8217;in oluşturulup çalıştırılmasından itibaren 20 saniye sonra threadimiz bitecek ve WaitForSingleObject fonksiyonunun hemen altındaki ShowMessage kodumuz çalışabilecektir. Yukarıdaki kodu biraz değiştirelim ve belirli bir zaman bekleme kodu yazalım:</p>
<pre class="brush: delphi">
procedure TForm1.Button1Click(Sender: TObject);
var
  mThread : TMyThread;
  RetVal    : DWord;
begin
  mThread := TMyThread.Create;
  RetVal := WaitForSingleObject(mThread.Handle, 10000); // 10 saniye bekle..
  ShowMessage(&#039;Thread bitti&#039;);
end;
</pre>
<p>Kodumuzun bu yeni halinde thread&#8217;in çalışma zamanında herhangi bir değişiklik yapmadık ancak bekleme kodumuzu 10 saniye olarak belirttik. Bunu yapmamızın sebebi, WaitForSingleObject metodunun dönüş değerlerini size anlatabilmek içindi. Normalde thread&#8217;lerimiz içinde 20 sn. bekle gibi bir kod yazmayız, dolayısı ile genellikle thread&#8217;lerimizin çalışma süresinin ne kadar olacağını kestiremeyiz. Yukarıdaki gibi WaitForSingleObject&#8217;e 10 saniye beklemesini söylediğimiz kullanımlarda, acaba gerçekten bu zaman zarfında thread&#8217;in bittiğini nereden bileceğiz ?</p>
<p>Bizim şu andaki örneğimizde thread&#8217;imiz 20 saniye sürüyordu, oysa biz 10 saniye bekledikten sonra çıktık. İşte bu sorumuza WaitForSingleObject&#8217;ten geriye dönen değerin kontrolü vasıtası ile yanıt bulabileceğiz. WaitForSingleObject yada WaitForMultipleObjects&#8217;den dönen değerler aşağıdaki gibidir:</p>
<pre class="brush: delphi">
  STATUS_WAIT_0                   = $00000000;
  STATUS_TIMEOUT                 = $00000102;

  WAIT_OBJECT_0 = ((STATUS_WAIT_0 ) + 0 );
  WAIT_TIMEOUT = STATUS_TIMEOUT;
</pre>
<p>Eğer WaitForSingleObject&#8217;in beklediği işletim sistemi nesnesinin bitmesi sebebi ile WaitForSingleObject beklemeyi bıraktı ise; bize fonksiyonun geri dönüş değeri <span style="color: #ff9900;">WAIT_OBJECT_0</span> olacaktır. Aksi durumda, yani işletim sistemi nesnesi hâla tamamlanmamış ancak bizim belirttiğimiz bekleme zamanı dolmuş ise o zamanda <span style="color: #ff9900;">WAIT_TIMEOUT</span> değerini elde edeceğiz. Yeni kodumuz aşağıdaki gibi olacak;</p>
<pre class="brush: delphi">
procedure TForm1.Button1Click(Sender: TObject);
var
  mThread : TMyThread;
  RetVal    : DWord;
begin
  mThread := TMyThread.Create;
  RetVal := WaitForSingleObject(mThread.Handle, 10000); // 10 saniye bekle..
  case RetVal of
    WAIT_OBJECT_0 : ShowMessage(&#039;Thread tamamlanmış.&#039;);
    WAIT_TIMEOUT  : ShowMessage(&#039;Thread hâla çalışıyor ancak 10 saniye dolmuş.&#039;);
  end;
end;
</pre>
<p>Sizin de kod örneklerinden anlayabileceğiniz gibi, WaitForSingleObject yada WaitForMultipleObjects fonksiyonları bekleme yaptıkları için çağırıldıkları ortamı(thread) bloke edeceklerdir. Bizim örneğimizde WaitForSingleObject formumuzun üstündeki bir button&#8217;un altında çağırıldığına göre uygulamamızın main thread&#8217;i bloke olacaktır. 10 saniyenin geçmesine müteakip main thread&#8217;imiz yine devrede olacaktır. Tabii WaitForSingleObject fonksiyonunun ikinci parametresini INFINITE olarak geçerseniz örneğimize göre uygulamamızın main thread&#8217;i 20 saniye boyunca blok olacaktır.</p>
<p><a href="http://www.tugrulhelvaci.com/?p=434">Bir önceki makalemizde</a> TThread sınıfının WaitFor isimli metodunun aynı şeyi yaptığından bahsetmiştik. Ancak o makalede de görebileceğiniz gibi, WaitFor metodu şu an sıkıntılı. Dolayısı ile yukarıda saydığımız işletim sistemi nesnelerini şimdilik WaitForSingleObject ile beklemeniz daha iyi olacaktır.</p>
<p>WaitForSingleObject için yazdığımız herşey WaitForMultipleObjects içinde geçerlidir. Ancak WaitForMultipleObjects&#8217;in biraz daha farklı yönleri vardır. Farzedelim ki, uygulamanız içinde birden fazla threadiniz var ve bu thread&#8217;lerin hepsinin bitmesini beklemek istiyorsunuz. Her thread için WaitForSingleObject kullanmak hiç de mantıklı olmazdı. O zaman imdadınıza WaitForMultipleObjects yetişecek. Bir önceki örneğimizi birden fazla thread&#8217;e göre yeniden yazalım ve bu sefer WaitForMultipleObjects kullanalım:</p>
<pre class="brush: delphi">
TMyOtherThread = class(TThread)
private
  fSecond : DWord;
protected
  procedure Execute; override;
public
  constructor Create(const Second : DWord);
end;

{ TMyOtherThread }

constructor TMyOtherThread.Create(const Second : DWord);
begin
  inherited Create(true);
  FreeOnTerminate := true;
  fSecond := Second;

  Resume;
end;

procedure TMyOtherThread.Execute;
begin
  inherited;

  Sleep(fSecond * 1000);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  mThread1,
  mThread2,
  mThread3,
  mThread4 : TMyOtherThread;

  Handles    : array[0..3] of THandle;

  StartTime,
  StopTime  : String;
  RetVal    : DWord;
begin
  StartTime := TimeToStr(Time);

  mThread1 := TMyOtherThread.Create(15);
  Handles[0] := mThread1.Handle;

  mThread2 := TMyOtherThread.Create(10);
  Handles[1] := mThread2.Handle;

  mThread3 := TMyOtherThread.Create(20);
  Handles[2] := mThread3.Handle;

  mThread4 := TMyOtherThread.Create(25);
  Handles[3] := mThread4.Handle;

  RetVal := WaitForMultipleObjects(4, @Handles, true, INFINITE);

  case RetVal of
    WAIT_OBJECT_0 	: Memo1.Lines.Add(&#039;Thread 0 tamamlandı.&#039;);
    WAIT_OBJECT_0 + 1	: Memo1.Lines.Add(&#039;Thread 1 tamamlandı.&#039;);
    WAIT_OBJECT_0 + 2	: Memo1.Lines.Add(&#039;Thread 2 tamamlandı.&#039;);
    WAIT_OBJECT_0 + 3	: Memo1.Lines.Add(&#039;Thread 3 tamamlandı.&#039;);
  end;

  StopTime := TimeToStr(Time);

  Memo1.Lines.Add(&#039;Start Time:&#039; + StartTime + &#039;-Stop Time:&#039; + StopTime);
  Memo1.Lines.Add(InttoStr(RetVal));
end;
</pre>
<p>Yukarıdaki örneğimizde, 4 adet TMyOtherThread oluşturuluyor. Birinci thread 15 sn. , ikinci thread 10 sn. , üçüncü thread 20 sn. ve son thread ise 25 sn. çalışacak şekilde ayarlanıyor. Ardından WaitForMultipleObjects fonksiyonumuza 4 adet thread&#8217;i beklemesi gerektiği ve bu thread&#8217;lerin handle&#8217;larının Handles isimli array tipli değişkende olduğu söyleniyor. Fonksiyonun üçüncü parametresi bütün thread&#8217;lerin beklenip beklenmeyeceğini ifade ediyor. Biz bu parametreye true geçerek tüm thread&#8217;lerin beklenmesini istediğimizi söylüyoruz. Dördüncü ve son parametre ne kadar süre ile beklememiz gerektiği bilgisini içeriyor. Bu parametreye INFINITE girerek tüm thread&#8217;lerin bitmesine yetecek kadar zaman beklememiz gerektiğini ifade etmiş oluyoruz.</p>
<p>Bu durumda, TMemo türündeki Memo1 nesnemizde iki zaman aralığının 25 saniye olduğunu göreceksiniz. Çünkü, en uzun zaman çalışan thread 25 saniye sürüyor. Tüm thread&#8217;leri bekleyeceğimizi söylediğimiz içinde bekleyeceğimiz süre en uzun süre çalışan thread&#8217;in çalışma süresi kadar yani 25 saniye olmuş oluyor.</p>
<p>Yukarıdaki örneğimizde WaitForMultipleObjects fonksiyonundan geriye dönen değeri sakladığımız RetVal isimli değişkenin değerini bir case ifadesinde kontrol ettiğimizi göreceksiniz. WaitForMultipleObjects fonksiyonu tüm thread&#8217;lerin bekleneceğini anlatan üçüncü parametresine true geçilmesi durumunda her zaman tüm thread&#8217;lerin bitmesini bekleyeceği için geriye WAIT_OBJECT_0 döndürecektir.(Son parametrenin INFINITE olması halinde, aksi durumda belirtilen zaman yeterli gelmez ise WAIT_TIMEOUT döndürür.)</p>
<p>Peki tüm thread&#8217;lerin bitmesini beklemez isek ne olur ? Bu durumda WaitForMultipleObjects fonksiyonunun üçüncü parametresine false geçmemiz gerekir. Bunu yaptığımızda, <span style="color: #ff9900;">çalışma süresi en kısa olan thread işini bitirdiğinde WaitForMultipleObjects artık beklemekten vazgeçecektir</span>. Geriye dönen değer ise biten thread&#8217;in dizideki <span style="color: #ff9900;">indis</span> numarası olacaktır. Yukarıdaki örneğimizde;</p>
<li>Thread 1-> Çalışma Zamanı : 15 sn. Dizideki Indis No: 0</li>
<li>Thread 2-> Çalışma Zamanı : 10 sn. Dizideki Indis No: 1</li>
<li>Thread 3-> Çalışma Zamanı : 20 sn. Dizideki Indis No: 2</li>
<li>Thread 4-> Çalışma Zamanı : 25 sn. Dizideki Indis No: 3</li>
<p>Çalışma süresi en az olan thread &#8220;Thread 2&#8243; olacağından WaitForMultipleObjects bize <span style="color: #ff9900;">WAIT_OBJECT_0 + 1</span> döndürecektir. Bu mekanizma vasıtası ile birden fazla işletim sistemi nesnesinin beklendiği durumda hangi nesnenin sonlandığını bulabilirsiniz.</p>
<p><span style="color: #ffcc00;"><strong>Mutex</strong></span></p>
<p>Critical Section&#8217;lardan sonra bu mekanizmaya son derece benzeyen Mutex&#8217;lere giriş yapabiliriz. Mutex&#8217;ler tüm senkronizasyon mekanizmalarında olduğu gibi kod bloklarımızı birden fazla thread&#8217;in girmesi vesilesi ile oluşabilecek bozulmalardan korurlar. Aynı amaca critical section&#8217;larında hizmet ettiğini söylemiştik. Peki mutex&#8217;ler de aynı şeyi yapıyor ise neden varlar ?</p>
<p>Aslında mutex&#8217;ler bire bir critical section&#8217;ların yaptıklarını yaparlar. Ancak fazladan güzel özelliklere sahiptirler. Bu özellikleri anlatmaya geçmeden evvel herzamanki gibi C++ ve Delphi tanımlarımıza yakından bakalım:</p>
<pre class="brush: c++">
HANDLE CreateMutex(
  LPSECURITY_ATTRIBUTES lpMutexAttributes,
  BOOL bInitialOwner,
  LPCTSTR lpName
);

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

BOOL ReleaseMutex(
  HANDLE hMutex
);
</pre>
<p>ve Delphi tanımları:</p>
<pre class="brush: delphi">
  function CreateMutex(lpMutexAttributes: PSecurityAttributes; bInitialOwner: BOOL; lpName: PChar): THandle; stdcall;
  function OpenMutex(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PChar): THandle; stdcall;
  function ReleaseMutex(hMutex: THandle): BOOL; stdcall;
</pre>
<p>Bir mutex senkronizasyon nesnesi CreateMutex API&#8217;si ile oluşturulur ve CloseHandle API&#8217;si ile de işletim sisteminden silinir. Mutex&#8217;lerin critical section&#8217;lara son derece benzediğini söylemiştik. Hatırlarsanız critical section&#8217;larda InitializeCriticalSection, DeleteCriticalSection, EnterCriticalSection ve LeaveCriticalSection metodlarını kullanıyorduk. Mutex ve critical section fonksiyonlarının benzerlikleri aşağıdaki tablodan gözlemlenebilir:</p>
<table style="border-collapse:collapse;border:none" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td style="border:solid #3366FF 1.0pt;background:#99CCFF; padding:0in 5.4pt 0in 5.4pt" valign="top"><strong><span style="font-size:9.0pt;font-family:">Critical Section</span></strong></td>
<td style="border:solid #3366FF 1.0pt;border-left:none;background:#99CCFF;padding:0in 5.4pt 0in 5.4pt" valign="top"><strong><span style="font-size:9.0pt;font-family:">Mutex</span></strong></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">InitializeCriticalSection</span></td>
<td style="border-top:none;border-left:none;border-bottom:solid #3366FF 1.0pt; border-right:solid #3366FF 1.0pt;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">CreateMutex / OpenMutex</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#FFCC99;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">EnterCriticalSection</span></td>
<td style="border-top:none;border-left:none;border-bottom:solid #3366FF 1.0pt; border-right:solid #3366FF 1.0pt;background:#FFCC99;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">WaitForSingleObject</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">LeaveCriticalSection</span></td>
<td style="border-top:none;border-left:none;border-bottom:solid #3366FF 1.0pt;border-right:solid #3366FF 1.0pt;background:#E0E0E0;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">ReleaseMutex</span></td>
</tr>
<tr>
<td style="border:solid #3366FF 1.0pt;border-top:none;background:#FFCC99;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">DeleteCriticalSection</span></td>
<td style="border-top:none;border-left:none;border-bottom:solid #3366FF 1.0pt;border-right:solid #3366FF 1.0pt;background:#FFCC99;padding:0in 5.4pt 0in 5.4pt" valign="top"><span style="font-size:9.0pt;font-family:">CloseHandle</span></td>
</tr>
</tbody>
</table>
<p>Tüm bu bilgilerin ışığında bir critical section&#8217;un yaptığı işi mutex ile yapan bir örneği inceleyebiliriz;</p>
<pre class="brush: delphi">
type
TMutexThread = class(TThread)
private
  fName,
  fStartValue,
  fStopValue	: String;
protected
  procedure Execute; override;
public
  constructor Create(const AName : String);
  destructor Destroy; override;

  property StartValue : String read fStartValue;
  property StopValue 	: String read fStopValue;
  property Name		: String read fName;
end;

var
  Form1: TForm1;
  MutexHandle   : THandle;

implementation

procedure TForm1.FormCreate(Sender: TObject);
begin
  MutexHandle := CreateMutex(nil, false, nil);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  CloseHandle(MutexHandle);
end;

{ TMutexThread }
constructor TMutexThread.Create(const AName: String);
begin
  inherited Create(true);
  FreeOnTerminate := true;

  fName := AName;

  Resume;
end;

destructor TMutexThread.Destroy;
begin
  form1.Memo1.Lines.Add(&#039;Mutex Name:&#039; + Name + &#039; Start Time:&#039; + StartValue + &#039; Stop Time:&#039; + StopValue);

  inherited;
end;

procedure TMutexThread.Execute;
begin
  inherited;

  WaitForSingleObject(MutexHandle, INFINITE);
  fStartValue := TimeToStr(Time);

  Sleep(10000);

  fStopValue := TimeToStr(Time);
  ReleaseMutex(MutexHandle);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  mMutex1,
  mMutex2 : TMutexThread;
begin
  mMutex1 := TMutexThread.Create(&#039;Birinci Mutex&#039;);
  Sleep(2000); // 2 sn bekle..
  mMutex2 := TMutexThread.Create(&#039;İkinci Mutex&#039;);
end;
</pre>
<p>Yukarıdaki örneğimiz tıpkı critical section örneğindeki gibi çalışacaktır. &#8220;Birinci Mutex&#8221; çalışmasına başlayacak ve WaitForSingleObject ile global MutexHandle değişkeni vasıtası ile kapının açık olup olmadığını sorgulayacak ve kapı açık olduğundan hemen koda girecektir. &#8220;İkinci Mutex&#8221; 2 saniye sonra birincisi gibi korumalı koda girmeye çalışacak ama kapı kapalı olduğu için beklemeye başlayacaktır. &#8220;Birinci Mutex&#8221; thread&#8217;i ReleaseMutex çağrısı yapar yapmaz &#8220;İkinci Mutex&#8221; thread&#8217;i korumalı koda girebilecektir. Örnek uygulamamızın çıktısı:</p>
<li>Mutex Name:Birinci Mutex Start Time:20:44:28 Stop Time:20:44:38</li>
<li>Mutex Name:İkinci  Mutex Start Time:20:44:38 Stop Time:20:44:48</li>
<p>biçiminde olacaktır. Görüldüğü gibi &#8220;İkinci Mutex&#8221; birincisini beklemiş ve ardından çalışmaya devam etmiştir.</p>
<p>Bu noktaya kadar mutex&#8217;lerin critical section&#8217;lardan bir farkı görünmüyor. O halde neden mutex kullanacağız ? <span style="color: #ff9900;">Mutex&#8217;lerin esas gücü, senkronizasyon mekanizmasını process&#8217;ler arasında da yapabilmesinden geliyor.</span> CreateMutex tanımını hatırlayacak olursanız, 3ncü ve son parametresinde bir isim alabildiğini gözlemleyeceksiniz. Bu parametreye geçerli bir isim verilir ise, mutex&#8217;ler işletim sistemi üzerindeki birden fazla process arasında senkronizasyon yapabilme yeteneğine kavuşur.</p>
<p>Bazı zamanlarda thread ile erişeceğimiz kritik kodlar, işletim sistemi üzerindeki bazı kaynaklara tekil erişim gerektirirler. Örneğin; makinanıza programlanabilme imkanına sahip bir donanımın bağlı olduğunu düşünelim. Bu herhangi bir donanım olabilir. Diyelim ki, bir modeminiz var ve siz bu modemin BIOS&#8217;unu güncelleyebilen bir program yazdınız. Modem&#8217;in BIOS&#8217;u güncelleme işlemi sürer iken bir başka güncelleme isteğinin modem&#8217;e yönlendirilmemesi gerekir. Yazdığınız uygulamanın derlenmiş halinin ModemBIOS.exe olduğu düşünüldüğünde, bu uygulamanın içinde Modem&#8217;e erişip BIOS&#8217;u güncelleyen kodun bir daha çalıştırılmamasını sağlayabilirsiniz. Ancak sizin uygulamanızı kullanan kullanıcının ModemBIOS.exe programını bir tane daha açması durumunda ne olur ?</p>
<p>Bu durumda ModemBIOS.exe process&#8217;inden o anda çalışan iki adet olur. Ve her bir process kendi hafıza alanına sahip olduğu için , process&#8217;ler birbirlerinin kritik kod bloklarını bilemezler. Bu gibi bir durumda, ikinci ModemBIOS.exe uygulamanızda modem&#8217;in BIOS&#8217;unu güncellemeye çalışabilir ve geri dönülmesi zor donanımsal hatalara neden olabilirsiniz.</p>
<p>İşte tam bu noktada mutex&#8217;lerin gücü devreye girer. Birinci uygulamanın mutex&#8217;i oluşturduğunu ve modem&#8217;in bios&#8217;unu kritik kod bloğunda güncellediğini düşünelim. İkinci uygulama artık CreateMutex ile değil, OpenMutex ile mutex&#8217;e verilen isim vasıtası ile mutex&#8217;i açmaya çalışır. Bu durumda elde edilecek mutex handle&#8217;ı birinci uygulamanın create ettiği handle olacaktır.! Elde edilen bu handle ile BIOS&#8217;u güncelleyecek olan kod bloğuna girmeye çalışacak olan ikinci uygulamamız ; birinci uygulama o kod bloğundan çıkmadan içeriye giremeyecektir. Bu durumu simüle eden bir örnek yapalım arzu ederseniz;</p>
<pre class="brush: delphi">
type
TMutexThread = class(TThread)
private
  fName,
  fStartValue,
  fStopValue	: String;
protected
  procedure Execute; override;
public
  constructor Create(const AName : String);
  destructor Destroy; override;

  property StartValue : String read fStartValue;
  property StopValue 	: String read fStopValue;
  property Name		: String read fName;
end;

var
  Form1: TForm1;
  MutexHandle   : THandle;
implementation

{ TMutexThread }
constructor TMutexThread.Create(const AName: String);
begin
  inherited Create(true);
  FreeOnTerminate := true;

  fName := AName;

  Resume;
end;

destructor TMutexThread.Destroy;
begin
  form1.Memo1.Lines.Add(&#039;Mutex Name:&#039; + Name + &#039; Start Time:&#039; + StartValue + &#039; Stop Time:&#039; + StopValue);

  inherited;
end;

procedure TMutexThread.Execute;
begin
  inherited;

  WaitForSingleObject(MutexHandle, INFINITE);
  fStartValue := TimeToStr(Time);

  Sleep(10000);

  fStopValue := TimeToStr(Time);
  ReleaseMutex(MutexHandle);
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  Error : DWord;
begin
  MutexHandle := OpenMutex(MUTEX_ALL_ACCESS, false, PAnsiChar(&#039;Modem BIOS&#039;));
  if MutexHandle = 0 then
  begin
    Memo1.Lines.Add(&#039;Mutex bulunamadı, oluşturulacak.&#039;);
    MutexHandle := CreateMutex(nil, false, PAnsiChar(&#039;Modem BIOS&#039;));
    Error := GetLastError();

    if Error = ERROR_INVALID_HANDLE then
      Memo1.Lines.Add(&#039;Modem BIOS ismi daha önce mutex harici başka bir senkronizasyon nesnesinde kullanılmış.!&#039;);
  end
  else Memo1.Lines.Add(&#039;Mutex bulundu ve OpenMutex ile açıldı.&#039;);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  CloseHandle(MutexHandle);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  mMutex1,
  mMutex2 : TMutexThread;
begin
  Label1.Caption := &#039;Başlangıç Zamanı:&#039; + TimeToStr(Time);
  mMutex1 := TMutexThread.Create(Edit1.Text);
  Sleep(2000); // 2 sn bekle..
  mMutex2 := TMutexThread.Create(Edit2.Text);
end;
</pre>
<p>Yukarıdaki örneğimizde, button1&#8242;in OnClick olay yöneticisinde iki adet thread çalıştırılıyor. Bu thread&#8217;ler mutex senkronizasyon nesnelerini kullanıyorlar. Bu uygulama birden fazla sefer çalıştırıldığında ve ilgili button&#8217;a basıldığında çalışacak olan kodlar birbirlerini bekleyeceklerdir. Mutex&#8217;leri critical section&#8217;lardan farklı kılan en önemli özellik budur. Çalışacak olan thread&#8217;ler hangi process&#8217;de olurlarsa olsunlar aynı anda sadece bir tanesinin çalışmasını sağlamış olduk.</p>
<p>Formumuzun OnCreate olayında &#8220;Modem BIOS&#8221; olarak isimlendirdiğimiz bir mutex&#8217;i OpenMutex API&#8217;si vasıtası ile açmaya çalışıyoruz. Eğer bu isimde bir mutex daha önce oluşturuldu ise, OpenMutex fonksiyonu oluşturulan mutex&#8217;in handle&#8217;ını döndürür aksi durumda sıfır döndürecektir. Process&#8217;ler arası senkronizasyonun en can alıcı noktası da burasıdır. Mutex&#8217;lerin bu özelliği aynı zamanda sıklıkla; çalıştırılan uygulamanın birden fazla örneğinin olmaması için de kullanılmaktadır.</p>
<p>Eğer OpenMutex daha önce oluşturulmuş bir mutex bulamaz ise, o zaman mutex&#8217;i CreateMutex API&#8217;si vasıtası ile oluşturma yoluna gidiyoruz. CreateMutex fonksiyonunundan hemen sonra işletim sistemi fonksiyonlarından olan <span style="color: #ff9900;">GetLastError</span> ile son hata durumunu kontrol ediyoruz. CreateMutex API&#8217;si, kendisine geçilen isim ile(örneğimizde &#8220;Modem BIOS&#8221;) daha önce oluşturulmuş mutex harici bir nesne var(event, semaphor gibi) ise <span style="color: #ff9900;">ERROR_INVALID_HANDLE</span> değerini döndürecektir. Çünkü, event, semaphor, mutex gibi senkronizasyon nesneleri aynı isimlendirme alanını ortak kullanırlar.</p>
<p>Mutex&#8217;imizi formumuzun OnCreate olayında açtıktan yada oluşturduktan sonra mutex senkronizasyon nesneleri ile korumaya alınmış thread kodlarımızın çalıştırılmasını sağlıyoruz. Aşağıda yayınlayacağımız video&#8217;dan da görebileceğiniz gibi; birden fazla aynı uygulama çalıştırıldığında button&#8217;lara basma sırasına göre thread&#8217;ler sıraya girecekler ve hangi uygulamadan çalıştırıldıklarından bağımsız olarak birbirlerini bekleyebileceklerdir.</p>
<div id="media"><object width="640" height="498" data="http://www.tugrulhelvaci.com/wp-content/uploads/Mutex_controller.swf" type="application/x-shockwave-flash"><param name="id" value="csSWF" /><param name="quality" value="best" /><param name="bgcolor" value="#1a1a1a" /><param name="allowfullscreen" value="true" /><param name="scale" value="showall" /><param name="allowscriptaccess" value="always" /><param name="flashvars" value="autostart=false&amp;thumb=http://www.tugrulhelvaci.com/wp-content/uploads/FirstFrame.pngx&amp;thumbscale=45&amp;color=0x000000,0x000000" /><param name="src" value="http://www.tugrulhelvaci.com/wp-content/uploads/Mutex_controller.swf" /></object></div>
<p><span style="color: #ffcc00;"><strong>Semaphore</strong></span></p>
<p>Mutex senkronizasyon nesnelerinden sonra semaphore&#8217;lar hakkında da biraz konuşmamız gerekir. Bazen kritik olduğunu düşündüğümüz kod bloklarımıza sadece bir thread&#8217;in değil bizim belirlediğimiz sayıda thread&#8217;in girebilmesini de isteriz. Semaphore&#8217;ler bu amaca hizmet eden senkronizasyon mekanizmalarıdır. Semaphore&#8217;ler da tıpkı mutex&#8217;lerde olduğu gibi bir isimle yada isimsiz olarak oluşturulabilirler. İsimle oluşturulduklarında tahmin edeceğiniz üzere, birden fazla process&#8217;in birden fazla thread&#8217;ini sorunsuz bir şekilde senkronize edebilirler. Gelin herzamanki gibi önce tanımlarına bir göz gezdirelim:</p>
<pre class="brush: c++">
HANDLE CreateSemaphore(
  LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
  LONG lInitialCount,
  LONG lMaximumCount,
  LPCTSTR lpName
);

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

BOOL ReleaseSemaphore(
  HANDLE hSemaphore,
  LONG lReleaseCount,
  LPLONG lpPreviousCount
);
</pre>
<pre class="brush: delphi">
  function CreateSemaphore(lpSemaphoreAttributes: PSecurityAttributes;
  lInitialCount, lMaximumCount: Longint; lpName: PChar): THandle; stdcall;

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

  function ReleaseSemaphore(hSemaphore: THandle; lReleaseCount: Longint;
  lpPreviousCount: Pointer): BOOL; stdcall;
</pre>
<p>Bir semaphore nesnesi, tıpkı mutexlerde olduğu gibi CreateSemaphore ile oluşturulur ve CloseHandle ile işletim sisteminden silinir.  Mutex&#8217;lerde olduğu gibi semaphore&#8217;lar da da WaitForSingleObject kritik kod bloğunun başında yer alır. Kritik kod bloğu ReleaseSemaphore ile sonlandırılır. Semaphore nesnelerinin en önemli özelliğinin bir sayaç mekanizmasına sahip olduğunu ifade etmiştik. CreateSemaphore fonksiyonunun ikinci ve üçüncü parametreleri son derece önemlidir. İkinci parametre semaphore sayacının başlangıç değerini, üçüncü parametre ise kritik koda girebilecek maksimum thread sayısını ifade eder. Bu parametrelerin sahip olabilecekleri değerlere ilişkin kurallar aşağıda belirtildiği gibidir.</p>
<pre class="brush: delphi">
  lInitialCount &gt;= 0 veya lInitialCount &lt;= lMaximumCount
  lMaximumCount &gt; 0
</pre>
<p>Her WaitForSingleObject çağrısı semaphore mekanizmasında sayacın bir azaltılmasını sağlar ve yine her ReleaseSemaphore çağrısı sayacın bir arttırılmasına neden olur.Örneğin;</p>
<pre class="brush: delphi">
var
  SemaphoreHandle : THandle;
begin
  SemaphoreHandle := CreateSemaphore(nil, 2, 2, PAnsiChar(&#039;Semaphore Test&#039;)); // Sayaç değeri 2&#039;dir.

  WaitForSingleObject(SemaphoreHandle, INFINITE); // Sayaç değeri 1&#039;e iner.
  ..
  ..
  ReleaseSemaphore(SemaphoreHandle, 1, nil); // Sayaç değeri yeniden 2&#039;ye çıkar.
end;
</pre>
<p>Yukarıda gördüğünüz basit yapıda olduğu gibi bir semaphore sizin belirttiğiniz sayaç değeri ile başlar ve her WaitForSingleObject gördüğünde bu sayaç değeribi bir azaltır. Sayaç değeri sıfıra ulaştığında içeri girmeye çalışacak olan thread&#8217;ler bekletilmeye başlanır. Bu sayede kritik kod bloğuna sizin belirttiğiniz sayıda thread&#8217;in girmesi sağlanmış olur. Her ReleaseSemaphore çağrısı sayacı 1 arttıracaktır. Sayaç sıfırdan farklı bir değere ulaşır ulaşmaz, beklemekte olan thread&#8217;lerden bir tanesi hemen kritik kod bloğuna girecektir. Semaphore&#8217;larla ilgili kod örneğine geçmeden önce, OpenSemaphore fonksiyonunun birinci parametresindeki erişim sabitlerinin Delphi 7&#8242;de tanımlanmamış olduğunu hatırlatmakta fayda görüyorum. Delphi 2009&#8242;dan aldığım sabit tanımın Delphi 7 kullanan arkadaşlarımız için faydalı olacağına inanıyorum;</p>
<pre class="brush: delphi">
  SEMAPHORE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE or $3);
</pre>
<p>Şimdi isterseniz, sayaç mekanizması vasıtası ile kritik kod bloklarını senkronize eden semaphore&#8217;ler ile ilgili basit bir örnek yapalım. Bu örnek mutex&#8217;lerle son derece benzer olacak;</p>
<pre class="brush: delphi">
type
TSemaphoreThread = class(TThread)
private
  fName,
  fStartValue,
  fStopValue	: String;
protected
  procedure Execute; override;
public
  constructor Create(const AName : String);
  destructor Destroy; override;

  property StartValue : String read fStartValue;
  property StopValue 	: String read fStopValue;
  property Name		: String read fName;
end;

var
  Form1: TForm1;
  SemaphoreHandle : THandle;

implementation

procedure TForm1.FormCreate(Sender: TObject);
const
  SEMAPHORE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE or $3);
var
  Error : DWord;
begin
  SemaphoreHandle := OpenSemaphore(SEMAPHORE_ALL_ACCESS, false, PAnsiChar(&#039;Semaphore Test&#039;));
  if SemaphoreHandle = 0 then
  begin
    Memo1.Lines.Add(&#039;Semaphore bulunamadı, oluşturulacak.&#039;);
    SemaphoreHandle := CreateSemaphore(nil, 2, 2, PAnsiChar(&#039;Semaphore Test&#039;));
    Error := GetLastError();

    if Error = ERROR_INVALID_HANDLE then
      Memo1.Lines.Add(&#039;Semaphore Test ismi daha önce semaphore harici başka bir senkronizasyon nesnesinde kullanılmış.!&#039;);
  end else Memo1.Lines.Add(&#039;Semaphore bulundu ve OpenSemaphore ile açıldı.&#039;);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  CloseHandle(SemaphoreHandle);
end;

{ TSemaphoreThread }

constructor TSemaphoreThread.Create(const AName: String);
begin
  inherited Create(true);
  FreeOnTerminate := true;

  fName := AName;

  Resume;
end;

destructor TSemaphoreThread.Destroy;
begin
  form1.Memo1.Lines.Add(&#039;Semaphore Name:&#039; + Name + &#039; Start Time:&#039; + StartValue + &#039; Stop Time:&#039; + StopValue);

  inherited;
end;

procedure TSemaphoreThread.Execute;
begin
  inherited;

  WaitForSingleObject(SemaphoreHandle, INFINITE);
  fStartValue := TimeToStr(Time);

  Sleep(10000);

  fStopValue := TimeToStr(Time);
  ReleaseSemaphore(SemaphoreHandle, 1, nil);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  mThread1,
  mThread2,
  mThread3,
  mThread4	: TSemaphoreThread;
begin
  mThread1 := TSemaphoreThread.Create(&#039;Semaphore 1&#039;);
  Sleep(1000);

  mThread2 := TSemaphoreThread.Create(&#039;Semaphore 2&#039;);
  Sleep(1000);

  mThread3 := TSemaphoreThread.Create(&#039;Semaphore 3&#039;);
  Sleep(1000);

  mThread4 := TSemaphoreThread.Create(&#039;Semaphore 4&#039;);
end;
</pre>
<p>Yukarıdaki kod örneğimiz, 4 adet thread oluşturup çalıştırılmasını sağlıyor. Semaphore&#8217;umuz kritik kod bloğuna 2 adet thread&#8217;in girebileceği bilgisi ile oluşturulmuş durumda olduğu için aynı anda kritik kod bloğunda maksimum 2 thread&#8217;imiz olabilecektir. Diğer iki thread, kritik kod bloğuna giren thread&#8217;lerden dışarı çıkan olmadığı müddetçe bekleyeceklerdir.Kritik kod bloğundan bir thread çıkar çıkmaz bekleyen thread&#8217;lerden birisi hemen içeri girebilecektir. Semaphore&#8217;larda da isimlendirme özelliği olduğu için çalışan thread&#8217;lerin hangi process&#8217;de çalıştıklarının bir önemi yoktur. Bu teknikte process&#8217;ler arası senkronizasyon sayaç mekanizması ile sağlanmış olur.</p>
<p>Bundan sonraki hedefimizin Event ve Waitable Timer&#8217;lar olduğunu söyleyerek bu makaleyi neticelendirmek istiyorum müsaadeniz ile. Son derece uzun olduğu kanaatindeyim, dolayısı ile makaleyi daha da uzatmamak ve okunurluğu arttırabilmek adına diğer senkronizasyon mekanizmalarını bir sonraki makaleme taşıyacağım.</p>
<p>Bu makalemizde aslında thread konusunun basit bir konu olmadığı, ancak korkulara da yer olmadığını umarım anlatabilmişimdir. Thread&#8217;lerin diğer kodlama tekniklerinden tek farkı benim bakış açıma göre, sadece daha dikkatle kodlanmalarının zorunluluğu ve biraz işletim sistemi bilgisinin gerekliliğidir.</p>
<p>Sizlere bol multi-threaded&#8217;lı günler dilerim <img src='http://www.tugrulhelvaci.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Saygılar, sevgiler..</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tugrulhelvaci.com/?feed=rss2&amp;p=443</wfw:commentRss>
		<slash:comments>23</slash:comments>
		</item>
		<item>
		<title>TThread.WaitFor Bug..</title>
		<link>http://www.tugrulhelvaci.com/?p=434</link>
		<comments>http://www.tugrulhelvaci.com/?p=434#comments</comments>
		<pubDate>Thu, 25 Jun 2009 23:24:31 +0000</pubDate>
		<dc:creator>Tuğrul HELVACI</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Bug]]></category>
		<category><![CDATA[Thread]]></category>
		<category><![CDATA[WaitForSingleObject]]></category>

		<guid isPermaLink="false">http://www.tugrulhelvaci.com/?p=434</guid>
		<description><![CDATA[ Yakın bir zamanda TThread sınıfına yine işim düşmüştü. Yazacağım bir kod parçasında bir thread&#8217;in işinin bitmesini beklemem gerekiyordu. Kodumu bu şekilde geliştirirken Delphi&#8217;nin bir bug&#8217;una rastladım ve bu hatayı Delphi ürün müdürü Nick Hodges&#8216;a ilettim. Sorunun bir sonraki Delphi sürümünde ortadan kalkacağına inanıyorum.
 Kısaca sorunun ne olduğundan bahsettikten sonra, mail yazışmalarımızı ilgilenenler için paylaşacağım. [...]]]></description>
			<content:encoded><![CDATA[<p> Yakın bir zamanda <span style="color: #ff9900;">TThread</span> sınıfına yine işim düşmüştü. Yazacağım bir kod parçasında bir thread&#8217;in işinin bitmesini beklemem gerekiyordu. Kodumu bu şekilde geliştirirken Delphi&#8217;nin bir bug&#8217;una rastladım ve bu hatayı Delphi ürün müdürü <a href="http://blogs.embarcadero.com/nickhodges/">Nick Hodges</a>&#8216;a ilettim. Sorunun bir sonraki Delphi sürümünde ortadan kalkacağına inanıyorum.</p>
<p> Kısaca sorunun ne olduğundan bahsettikten sonra, mail yazışmalarımızı ilgilenenler için paylaşacağım. Bildiğiniz gibi TThread sınıfının <span style="color: #ff9900;">WaitFor</span> metodu bir thread&#8217;in işinin bitmesini beklemek için tasarlanmıştır. Ve görevini layıkı ile yerine getirir. Ancak, eğer thread kendi constructor&#8217;ında işi bittikten sonra yok edilsin ayarına set edildi ise o zaman bu bug ile karşılaşacaksınız. Thread&#8217;inizin constructor &#8216;ında <span style="color: #ff9900;">FreeOnTerminate := true</span> gibi bir satır kod yazdı iseniz ve thread&#8217;inizin bitmesini WaitFor ile bekliyorsanız, thread&#8217;inizin bitmesine müteakip hata ile karşılaşacaksınız. Bunun nedeni, Thread sınıfının destructor&#8217;ında işletim sistemi tarafından ayrılmış olan thread handle&#8217;ının <span style="color: #ff9900;">CloseHandle</span> ile serbest bırakılmasına karşılık WaitFor metodu içinde bu handle&#8217;ın kullanılmaya çalışılmasıdır. Sizlere bu sorun düzeltilinceye kadarki tavsiyem <span style="color: #ff9900;">WaitForSingleObject(myThread.Handle, INFINITE)</span> kodu ile bekleme yapmanızdır.</p>
<p> Delphi ürün müdürü ile yazışmalarımız aşağıdaki gibidir:<br />
<span id="more-434"></span></p>
<blockquote><p>
FW: TThread.WaitFor Bug.!‏<br />
From: 	Nick Hodges (Nick.Hodges@embarcadero.com)<br />
Sent: 	Wed 6/24/09 6:13 PM<br />
To: 	king_of_delphi@hotmail.com (king_of_delphi@hotmail.com)<br />
Thanks for the report!</p>
<p>From: Jason Sprenger<br />
Sent: Wednesday, June 24, 2009 11:04<br />
To: Seppy Bloom; Nick Hodges; Jason Vokes<br />
Cc: Thom Gerdes; Marty Thompson<br />
Subject: RE: TThread.WaitFor Bug.!</p>
<p>I&#8217;ve reviewed the bug report and attached to it a test case from the original program fragments.</p>
<p>&#8211;Jason</p>
<p>    From: Seppy Bloom<br />
    Sent: Monday, June 22, 2009 10:24 PM<br />
    To: Nick Hodges; Jason Vokes<br />
    Cc: Thom Gerdes; Marty Thompson; Jason Sprenger<br />
    Subject: RE: TThread.WaitFor Bug.!</p>
<p>    Raid #270371, so we can all track it&#8217;s progress.</p>
<p>    Seppy</p>
<p>    From: Nick Hodges<br />
    Sent: Monday, June 22, 2009 10:52 AM<br />
    To: Seppy Bloom; Jason Vokes<br />
    Cc: Thom Gerdes; Marty Thompson; Jason Sprenger<br />
    Subject: RE: TThread.WaitFor Bug.!</p>
<p>    Yes, here in this email.  <img src='http://www.tugrulhelvaci.com/wp-includes/images/smilies/icon_razz.gif' alt=':-P' class='wp-smiley' /> </p>
<p>        From: Seppy Bloom<br />
        Sent: Monday, June 22, 2009 10:02<br />
        To: Nick Hodges; Jason Vokes<br />
        Cc: Thom Gerdes; Marty Thompson; Jason Sprenger<br />
        Subject: RE: TThread.WaitFor Bug.!</p>
<p>        Is it reported somewhere? <img src='http://www.tugrulhelvaci.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>        Seppy</p>
<p>        From: Nick Hodges<br />
        Sent: Monday, June 22, 2009 9:43 AM<br />
        To: Jason Vokes<br />
        Cc: Seppy Bloom; Thom Gerdes; Marty Thompson<br />
        Subject: FW: TThread.WaitFor Bug.!</p>
<p>        Is there a bug here, folks?</p>
<p>        Nick</p>
<p>        From: Excellent Delphi [mailto:king_of_delphi@hotmail.com]<br />
        Sent: Monday, June 22, 2009 05:27<br />
        To: nick.hodges@borland.com; Nick Hodges<br />
        Subject: TThread.WaitFor Bug.!</p>
<p>         Hi Nick, sorry about this e-mail. I dont know where the new quality central is.</p>
<p>         Pls check the TThread.WaitFor method.</p>
<p>         TMyThread = class(TThread)<br />
         protected<br />
           procedure Execute; override;<br />
         public<br />
          constructor Create;<br />
         end;</p>
<p>         &#8230;.<br />
         &#8230;.<br />
         procedure TMyThread.Execute;<br />
         var<br />
           iCounter : Integer;<br />
           sVal : String;<br />
         begin<br />
            inherited;</p>
<p>            for iCounter := 0 to 1000000 do sVal := InttoStr(iCounter);<br />
         end;</p>
<p>         constructor TMyThread.Create;<br />
         begin<br />
           inherited Create(true);<br />
           FreeOnTerminate := true; // it will be a problem when u use waitfor for waiting thread&#8217;s termination<br />
         end;</p>
<p>         procedure TForm1.Button1Click(Sender : TObject);<br />
         var<br />
           mThread : TMyThread;<br />
         begin<br />
           mThread := TMyThread.Creat e;<br />
           mThread.Resume;<br />
           mThread.WaitFor; // when we use WaitForSingleObject(mThread.Handle, INFINITE) the problem never been seen,<br />
                                    // because GetExitCodeThread dont called.</p>
<p>           Caption := TimeToStr(Time);<br />
         end;</p>
<p>         In classes.pas ThreadProc method is following;</p>
<p>        function ThreadProc(Thread: TThread): Integer;<br />
        var<br />
          FreeThread: Boolean;<br />
        begin<br />
        {$IFDEF LINUX}<br />
          if Thread.FSuspended then sem_wait(Thread.FCreateSuspendedSem);<br />
        {$ENDIF}<br />
          try<br />
            if not Thread.Terminated then<br />
            try<br />
              Thread.Execute;<br />
            except<br />
              Thread.FFatalException := AcquireExceptionObject;<br />
            end;<br />
          fina lly<br />
            FreeThread := Thread.FFreeOnTerminate;<br />
            Result := Thread.FReturnValue;<br />
            Thread.DoTerminate;<br />
            Thread.FFinished := True;<br />
            SignalSyncEvent;<br />
            if FreeThread then Thread.Free;<br />
        {$IFDEF MSWINDOWS}<br />
            EndThread(Result);<br />
        {$ENDIF}<br />
        {$IFDEF LINUX}<br />
            // Directly call pthread_exit since EndThread will detach the thread causing<br />
            // the pthread_join in TThread.WaitFor to fail.  Also, make sure the EndThreadProc<br />
            // is called just like EndThread would do. EndThreadProc should not return<br />
            // and call pthread_exit itself.<br />
            if Assigned(EndThreadProc) then<br />
              EndThreadProc(Result);<br />
            pthread_exit(Pointer(Result));<br />
        {$ENDIF}<br />
          end;<br />
        end;</p>
<p>         When the Thread.Free call, thread handle will be invalid by CloseHandle, but WaitFor method still waits this thread. At the end of WaitFor method, it will try to call  CheckThreadError(GetExitCodeThread(H[0], Result));</p>
<p>         But H[0] is now invalid at this point, so we will give an exception. If you write &#8220;if FreeThread then Thread.Free;&#8221; line after the EndThread(Result) line, problem will be avoid.</p>
<p>        Note: Problem is the same Delphi 7..2009<br />
        Best Regards<br />
        Tuğrul HELVACI
</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.tugrulhelvaci.com/?feed=rss2&amp;p=434</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Win32 &amp; .Net(Delphi-&gt;C#)</title>
		<link>http://www.tugrulhelvaci.com/?p=404</link>
		<comments>http://www.tugrulhelvaci.com/?p=404#comments</comments>
		<pubDate>Sun, 21 Jun 2009 02:29:41 +0000</pubDate>
		<dc:creator>Tuğrul HELVACI</dc:creator>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Programlama]]></category>
		<category><![CDATA[Veritabanı]]></category>
		<category><![CDATA[Win32]]></category>
		<category><![CDATA[COM]]></category>

		<guid isPermaLink="false">http://www.tugrulhelvaci.com/?p=404</guid>
		<description><![CDATA[ Aslında herşey Java adı verilen programlama dilinin doğuşuna kadar ilerliyor. Java, programlama dünyasına farklı bir perspektif katmıştı. Yazılan kodların applet&#8217;ler vasıtası ile web ortamlarında kullanılabilmesi yada Java Runtime ile değişik işletim sistemi platformlarında çalıştırılabiliyor olması onu günden güne daha popüler hâle getiriyordu.
 Microsoft, kendi işletim sistemlerinin yeryüzündeki tüm bilgisayarlarda kullanılamayacağının farkına vardığında; Java&#8217;ya karşı [...]]]></description>
			<content:encoded><![CDATA[<p> Aslında herşey Java adı verilen programlama dilinin doğuşuna kadar ilerliyor. Java, programlama dünyasına farklı bir perspektif katmıştı. Yazılan kodların applet&#8217;ler vasıtası ile web ortamlarında kullanılabilmesi yada Java Runtime ile değişik işletim sistemi platformlarında çalıştırılabiliyor olması onu günden güne daha popüler hâle getiriyordu.</p>
<p> Microsoft, kendi işletim sistemlerinin yeryüzündeki tüm bilgisayarlarda kullanılamayacağının farkına vardığında; Java&#8217;ya karşı bir önlem almak gerektiğini düşündü ve bu sayede veriye her ortamdan erişebilecek bir sistem planlamaya başladı. Ancak elbette Java&#8217;nın da hâla beceremediği gibi Microsoft&#8217;da platform bağımsızlığı hususunda başarılı olamadı. Zaten tasarımların gereği de bunu pek mümkün kılmıyordu. Java&#8217;da üretilen kodların bytecode&#8217;lara çevrilmesi ve JVM(Java Virtual Machine) adı verilen programlarla üzerinde çalıştığı platforma adapte edilmesindeki süreç, .Net&#8217;de de kendisine farklı isimlerle yer buldu.</p>
<p> .Net, ürettiği MSIL kodunu üzerinde çalıştığı platformun anlayacağı makina dili koduna ise JIT vasıtası ile çevirir. Bu tıpkı Java&#8217;nın JVM&#8217;ine benzer. Aradaki benzerlikleri saymaya kalksak emin olun sayfalarca yazı yazmamız gerekir. Benim bu makalede amacım bu iki platform bağımsız olduğunu iddia eden teknolojinin benzerliklerini ve farklılıklarını anlatmak değil. Bu bilgileri genel kültür amacı ile sunduktan sonra Delphi&#8217;nin bu teknolojiler ile iletişimi hakkında bilgi vermek ve <strong>gerçek platform bağımsız kodlamanın gelecekte Delphi ile olabileceğinin</strong> umudunu sizlerle paylaşmak.</p>
<p> Görüldüğü üzere gerek .Net gerekse de Java platform bağımsız native kod geliştiremiyorlar. Ürettikleri ara kodların, çeşitli işletim sistemlerinde yorumlanıp makina koduna çevrilmeleri gerekiyor. Dolayısı ile teoride bu teknolojileri kullanan uygulamaların, native uygulamalardan hızlı olması beklenmiyor.</p>
<p>  .Net, bilindiği üzere programlama dillerinden bağımsız bir platform. Framework adı verilen kod kütüphanelerinin tüm programlama dilleri tarafından ortak bir şekilde kullanılabilmesi, CTS denilen ortak tip sınıflarının .Net içinde olması, herhangi bir .Net destekli dilin bir diğer .Net destekli dil ile iletişimini son derece sorunsuz ve kolay hale getirmekte. Büyük çaplı proje ekiplerinin pek çok programcıya sahip olduğu gerçeği göz önüne alındığında, bu ekip üyelerinin herhangi bir .Net dilini bilmesi ve bu dil ile geliştirme yapmasının projeye olumsuz bir etkisinin olmaması elbette son derece güzel bir durum.<br />
<span id="more-404"></span><br />
 .Net Framework adı verilen kaba tabiri ile programlama kütüphanesi, şu aşamada 3.5 versiyonunda bulunuyor. Yakın bir zamanda 4.0 versiyonunun çıkması bekleniyor. Bildiğim kadarı ile Framework&#8217;teki sınıf tasarımları .Net framework 2.0&#8242;dan sonra herhangi bir değişikliğe uğramadı. Sadece üzerine yeni yetenekler eklendi. Kısaca .Net terminolojisinden bahsettikten sonra, Delphi&#8217;nin bu terminolojiye hangi mesafede olduğu hakkında yazmak isterim.</p>
<p> Delphi 7 , hâla pek çok Delphi kullanıcısının kullandığı ana IDE durumunda. Bunun stabilite ve hız gibi pek çok etkenleri var. Delphi, gerçek anlamda .Net ile 8 versiyonunda buluştu. Ancak Delphi 8, Microsoft&#8217;un .Net teknolojisine ilk desteği veren ürün olduğu için hiçte stabil değildi ve Delphi severler tarafından pek de tutulmadı. Ardından Borland, .Net platformuna desteğini Delphi 2005 ile sürdürdü. Ancak kişisel tecrübelerim ile söyleyebilirim ki, o da gerektiği ölçüde stabil değildi ve pek çok sorunu vardı. .Net desteği, 2006 ve 2007 sürümlerine kadar devam etti ve ardından büyük bir değişim ile karşılaştık.</p>
<p> Borland, programlama ürünlerini CodeGear isimli firmaya devretmiş ve ardından bu ürünleri Embarcadero isimli bir firma satın almıştı. Bu süreçte, artık Delphi ve diğer programlama ortamlarının .Net&#8217;e destek vermemesine karar verildi. Bu son derece yerinde bir karardı. Delphi, hayata ilk geldiği 1995 yılından bu zamana kadar native uygulama geliştirme konusunda, dünyada pek çok kez ödül almış bir ortam iken, .Net framework desteği adına adeta kendi kendine intihar etme kararı almıştı.</p>
<p> Günümüzde, .Net programlama adına Delphi yazım tarzına aşina olan programcıların Delphi Prism ürününü tercih etmesi isteniyor. Bu ürün; .Net framework ortamına %100 destek veren ve Delphi yazım tarzına son derece benzeyen Embarcadero firması programlama ortamları ailesinin bir başka ürünü.</p>
<p> Embarcadero, Delphi ürününü satın aldıktan sonra, beklentilerimizin aksine bu ürün ailesine son derece önem verdi ve ürünün eskide olduğu gibi popülerliğini yeniden kazanmasında büyük katkı sağlamaya başladı. Bu konuda sevgili arkadaşımız Sadettin POLAT&#8217;ın sitesindeki <a href="http://tr.delphipeak.com/2009/05/16/delphi-yol-haritasi.htm">makaleyi</a> okumanızı öneriyorum. </p>
<p> Konumuzun Win32 ve .Net olması münasebeti ile, kısa bir zaman sonra çıkması beklenen yeni Delphi sürümünün yani Delphi Weaver&#8217;ın çok önem verdiğim bir özelliğini de sizlerle paylaşmak istiyorum. Delphi Weaver&#8217;ın özelliklerinde listelenen ancak pek çok kişinin dikkatini cezbetmeyen, <span style="color: #ff9900;">&#8220;Seamless .NET <> Native communication&#8221;</span> özelliği eminim pek çok kişinin native uygulama geliştirebilecekleri ortamlara geçişinde etken olacaktır. Peki nedir bu &#8220;Seamless .NET <> Native communication&#8221; özelliği ?</p>
<p> Bu özellik hakkında henüz net bir şey söz konusu değil. Ancak, neler yapılabileceği hususunda ; <a href="http://www.atozed.com/CrossTalk/index.EN.aspx">burayı</a> ve bu <a href="news://news.atozed.com">haber grubunu</a> takip etmenizi tavsiye edebilirim.</p>
<p><span style="color: #ff9900;"> Kısaca; Win32 ortamında tamamen native kod geliştirirken, .Net framework fonksiyonalitesine herhangi bir COM bağımlılığı olmadan, %100 delphi kodları ile erişmek isterseniz bu teknoloji tam size göre demektir.</span> <img src='http://www.tugrulhelvaci.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p> Atozed Software&#8217;in üzerinde çalıştığı <span style="color: #ff9900;">CrossTalk</span> isimli ürün, Delphi 5,6,7..2009 altından .Net Framework&#8217;e erişebilmeniz için gereken tüm altyapıyı sağlayacak. Projenin lideri olan Chad Z. Hover ile yapmış olduğum görüşme neticesinde öğrendiğim şey ise beni daha da sevindirdi. CrosTalk ürünü Delphi Weawer ile birlikte gelecek. Yani birkaç paragraf önce belirttiğimiz &#8220;Seamless .NET <> Native communication&#8221; Delphi Weaver altındaki CrossTalk ürününü simgeliyor.</p>
<p>   Bu yeni teknolojinin Delphi&#8217;nin yükselmesinde büyük bir paydaya hizmet edeceği inancını taşıyorum. Ayrıca Delphi&#8217;nin hedefleri arasında <span style="color: #ff9900;">Cross platform code compilation</span> olduğunu da bilmenizi istiyorum. İşte gerçek platform bağımsız kodlama bizlerin hizmetine sunulacak. Delphi&#8217;de yazdığımız uygulamaları Windows, Linux yada MacOS gibi işletim sistemleri üzerinde çalışır halde göreceğiz. Elbette bu, .Net&#8217;in yada Java&#8217;nın yaptığı gibi değil, native derleme ile yapılacak.</p>
<p> Tüm bu anlatılanlar, belki de hâla afaki kalmış olabilir. CrossTalk&#8217;ın yahut Delphi Weaver içindeki &#8220;Seamless .NET <> Native communication&#8221; özelliğinin ne kadar önemli olduğu anlaşılmamış da olabilir. Bunun önemini anlatabilmek için, Win32&#8242;den .Net&#8217;e erişmeye çalışmak gerekir.</p>
<p> Bu makalede, Win32 ortamından .Net Framework&#8217;e erişeceğiz ve tüm bu zorlukları sizlerin gözleri önüne sereceğim. Öncelikle Visual Studio 2005 ile bir <span style="color: #ff9900;">Class Library(DLL)</span> oluşturacağız.Bu DLL&#8217;imiz SQL Server 2005&#8242;e erişim sağlayan bir kaç sınıftan ibaret olacak. <span style="color: #ff9900;">Ve Bu DLL&#8217;imizi Delphi 7 altından kullanacağız.</span></p>
<p> O halde kodlamaya başlayabiliriz. Öncelikle Visual Studio&#8217;muzu açıyoruz ve yeni bir Class Library projesi oluşturuyoruz. Ardından  Microsoft.SqlServer.ConnectionInfo, Microsoft.SqlServer.Smo, Microsoft.SqlServer.SmoEnum, Microsoft.SqlServer.SqlEnum library&#8217;lerini referans olarak ekliyoruz. Projemiz aşağıda göründüğü gibi olacaktır:</p>
<p><a href="http://www.tugrulhelvaci.com/wp-content/uploads/sqlclasslibrary_1.png"><img src="http://www.tugrulhelvaci.com/wp-content/uploads/sqlclasslibrary_1.png" alt="sqlclasslibrary_1" title="sqlclasslibrary_1" width="550" height="500" class="aligncenter size-full wp-image-409" /></a></p>
<p> Bu makalenin can alıcı noktası, .Net ortamında yazdığımız kodlarımızın dışarıdan(Win32 ortamından) kullanılabilmesi için <span style="color: #ff9900;">COM</span> programlamadan istifade edeceğimiz gerçeğidir. COM programlama kullanacağımıza göre, dışarıdan erişime açacağımız tüm sınıfların <span style="color: #ff9900;">ComVisible</span> attribute&#8217;u ile görünür hâle getirilmesi gerekir. Bir diğer önemli nokta ise, Class Library projemizin özelliklerinde gizlidir. Bu özel durumları aşağıdaki görsellerden izleyebilirsiniz:</p>
<p>Projemizin Application bölümünden <span style="color: #ff9900;">Assembly Information</span> buttonuna tıklanarak yapılalacak ayarlar:<br />
<a href="http://www.tugrulhelvaci.com/wp-content/uploads/sqlclasslibrary_2.png"><img src="http://www.tugrulhelvaci.com/wp-content/uploads/sqlclasslibrary_2.png" alt="sqlclasslibrary_2" title="sqlclasslibrary_2" width="390" height="379" class="aligncenter size-full wp-image-413" /></a></p>
<p>Projemizin Build sayfasında yapılacak ayarlar:<br />
<a href="http://www.tugrulhelvaci.com/wp-content/uploads/sqlclasslibrary_3.png"><img src="http://www.tugrulhelvaci.com/wp-content/uploads/sqlclasslibrary_3.png" alt="sqlclasslibrary_3" title="sqlclasslibrary_3" width="550" height="500" class="aligncenter size-full wp-image-412" /></a></p>
<p> Bu ayarların yapılmasına müteakip, C# tarafında SQL Server 2005&#8242;e bağlanan, ve bir veritabanının altındaki tüm tablolara erişim sağlayabileceğimiz kodları yazmaya başlamadan evvel anlatmamız gereken bir kaç husus daha var. Bunlardan en önemlisi; .Net framework platformundaki her veri türünü COM ortamında kullanmanın zorluklarıdır. Kullanmak istediğimiz veri türleri, ki bunların içinde özel sınıflar, framework sınıfları gibi tüm veri türleri ComVisible attribute&#8217;u ile işaretlenmiş olmalıdır. .Net ortamındaki tüm türlerin nasıl yönetileceğini dış uygulamalar bilemezler. Binlerce tür olduğu hesaplanırsa, bu türlerin hepsinin ComVisible ile işaretlenmesinin ne kadar zor olduğu da aşikârdır. </p>
<p> Makalemizin başlarında, temel veri türlerinin .Net içinde tanımlı olduğunu ifade etmiştik. Bu CTS adını almıştı ve pek çok programlama dilinin bir arada çalışabilmesi için son derece faydalı bir unsurdu. Ancak, şimdi bizim için bir sorun gibi duruyor. CTS içinde tanımlı olan bazı basit değişken türlerinin COM programlama da ComVisible ile görünür hâle getirilmeden de kullanılabildiğini ifade etmek sanırım sizleri biraz rahatlatacaktır ancak yine de yeteri derece de rahatlamamış olmanız gerekir. Çünkü, pek çok projemizde bu projemizde olduğu gibi, Integer, String vb. gibi basit veri türleri bizim için yeterli değildir. </p>
<p> .Net framework&#8217;ün <span style="color: #ff9900;">Smo</span> kütüphanesi ile Sql Server&#8217;a erişebildiğini biliyoruz. Ve bu Smo kütüphanesi içinde Sql Server içindeki hemen hemen her nesne için tanımlanmış bir veri tipi bulunmaktadır.(Genellikle bir sınıf). Peki biz bu veritiplerini dışarıya nasıl sunacağız ? Smo namespace&#8217;ininin tamamını mı ComVisible ile işaretleyeceğiz ?</p>
<p> Elbette hayır. Bu son derece uğraştırıcı ve zor bir durum olurdu. Bu sebeple, <span style="color: #ff9900;">Smo kütüphanesi içinde ilgilendiğimiz sınıflara karar verecek ve bu sınıflar için bir arabirim yazacağız. Ardından bu arabirimi görünür kılacağız</span> <img src='http://www.tugrulhelvaci.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p> Örneğin, biz bu uygulamamızda Sql Server 2005&#8242;e erişip herhangi bir veritabanının içindeki tüm <strong>tablo</strong>&#8216;lara ulaşmak istediğimize göre, Smo namespace&#8217;indeki <span style="color: #ff9900;">Table</span> sınıfına ait sahte bir interface düzenleyeceğiz. Gelin biraz da kodlayalım ki daha anlaşılır olsun:</p>
<pre class="brush: c#">
using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Common;
using System.Runtime.InteropServices;

namespace SQLClassLibrary
{
    [ComVisible(true)]
    public interface ITableInterface
    {
        DateTime CreateDate { get; }
        string Name { get; }
        long RowCount { get; }
        int ColumnCount { get; }
        int TriggerCount { get; }
    }

    public class TableClass : ITableInterface
    {
        private Table ActiveTable;

        public DateTime CreateDate
        {
            get
            {
                return ActiveTable.CreateDate;
            }
        }
        public string Name
        {
            get
            {
                return ActiveTable.Name;
            }
        }
        public long RowCount
        {
            get
            {
                return ActiveTable.RowCount;
            }
        }
        public int ColumnCount
        {
            get
            {
                return ActiveTable.Columns.Count;
            }
        }
        public int TriggerCount
        {
            get
            {
                return ActiveTable.Triggers.Count;
            }
        }

        public TableClass(Table tbl)
        {
            ActiveTable = tbl;
        }

        ~TableClass()
        {
            // Nesneleri yok etmeyi unutma.! GC ye güvenme..
        }
    }

    public class SQLServerClass
    {
    }
}
</pre>
<p> Yukarıdaki kod örneğimizde öncelikle <span style="color: #ff9900;">System.Runtime.InteropServices</span> isimli alanı using bloğuna eklediğimizi görüyorsunuz. ComVisible attribute&#8217;unu kullanmak için bunu yapmak durumundayız. Ardından Smo namespace&#8217;i içinde tanımlı olan Table isimli sınıfın ilgilendiğimiz birkaç tane property&#8217;sini tanımladığımız <span style="color: #ff9900;">ITableInterface</span> arabiriminin içinde görüyorsunuz. Bu arabirim, ComVisible ile işaretli durumda. Biz ileride Delphi üzerinden yazdığımız C# kodlarını kullanırken arabirim referanslarına erişeceğiz <img src='http://www.tugrulhelvaci.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>Ardından basit bir sınıf olan <span style="color: #ff9900;">TableClass</span> sınıfının ITableInterface arabirimini implemente ettiğini görüyorsunuz. Sizinde farkettiğiniz gibi, bu sınıf ComVisible ile işaretlenmemiş. Çünkü biz bu sınıfa ilgili arabirimi üzerinden erişim sağlayacağız. Dolayısı ile sınıfımız ITableInterface&#8217;i implemente ettiği için sadece arabirimi ComVisible ile görünür kılmak bizim için yeterli olacaktır.</p>
<p> Buradaki anafikri anlamanız çok önemli. .Net framework altındaki kompleks yapıları COM ortamından kullanabilmek için onların COM görünürlüğünün sağlanması gereklidir. Biz bu uygulamamızda .Net framework&#8217;ün Smo kütüphanesi içindeki Table isimli sınıfı bu sebeple dışarıya veremedik ! İlgili sınıfı(Table) ComVisible ile görünür kılamadığımız için, biz de bu sınıfın sahte bir kopyasına bir arabirim oluşturduk ve o kopyayı görünür kıldık. Meselenin özü aslında sadece bu kadar. Şimdi Sql Server 2005&#8242;e erişim sağlamasını düşündüğümüz sınıfımıza birer adet Connect ve Disconnect metodu yazmamız işimizi görecek midir ? Evet server&#8217;a bağlanmak için server&#8217;ın adına ve bağlantı şifresine ihtiyaç duyacağız ancak peki ya hangi veritabanından ilgili tabloları alacağız ? </p>
<p> Bu noktada bizim için bir de Smo namespace&#8217;indeki <span style="color: #ff9900;">Database</span> sınıfına bir interface uydurmamız gerekecek. Bu interface&#8217;de aşağıdaki gibi olacak:</p>
<pre class="brush: c#">
    [ComVisible(true)]
    public interface IDatabaseInterface
    {
        int ActiveConnections { get; }
        DateTime CreateDate { get; }
        DateTime LastBackupDate { get; }
        string Name { get; }
        double Size { get; }

        int StoredProcedureCount { get; }
        int TableCount { get; }
        int UserDefinedFunctionCount { get; }
        int ViewCount { get; }

        ITableInterface TableFromName(string TableName);
        ITableInterface TableFromIndex(int Index);
    }

    public class DatabaseClass : IDatabaseInterface
    {
        private Database ActiveDatabase;
        private Hashtable hTables;

        public int ActiveConnections
        {
            get
            {
                return ActiveDatabase.ActiveConnections;
            }
        }
        public string Collation
        {
            get
            {
                return ActiveDatabase.Collation;
            }
            set
            {
                ActiveDatabase.Collation = value;
                ActiveDatabase.Alter();
            }
        }
        public DateTime CreateDate
        {
            get
            {
                return ActiveDatabase.CreateDate;
            }
        }
        public DateTime LastBackupDate
        {
            get
            {
                return ActiveDatabase.LastBackupDate;
            }
        }
        public string Name
        {
            get
            {
                return ActiveDatabase.Name;
            }
        }
        public double Size
        {
            get
            {
                return ActiveDatabase.Size;
            }
        }
        public int StoredProcedureCount
        {
            get
            {
                return ActiveDatabase.StoredProcedures.Count;
            }
        }
        public int TableCount
        {
            get
            {
                return ActiveDatabase.Tables.Count;
            }
        }
        public int UserDefinedFunctionCount
        {
            get
            {
                return ActiveDatabase.UserDefinedFunctions.Count;
            }
        }
        public int ViewCount
        {
            get
            {
                return ActiveDatabase.Views.Count;
            }
        }

        public ITableInterface TableFromName(string TableName)
        {
            return (ITableInterface)hTables[TableName];
        }

        public ITableInterface TableFromIndex(int Index)
        {
            ITableInterface Result = null;

            int iCounter = 0;
            IDictionaryEnumerator TableEnum = hTables.GetEnumerator();
            while (TableEnum.MoveNext())
            {
                if (iCounter == Index)
                {
                    Result = (TableClass)TableEnum.Value;
                    break;
                }

                iCounter += 1;
            }

            return Result;
        }

        public DatabaseClass(Database db)
        {
            ActiveDatabase = db;
            hTables = new Hashtable();

            foreach (Table tbl in ActiveDatabase.Tables)
            {
                if (!tbl.IsSystemObject) hTables.Add(tbl.Name, new TableClass(tbl));
            }
        }
    }
</pre>
<p> Yukarıda <span style="color: #ff9900;">IDatabaseInterface</span> tanımını ve bu interface&#8217;i implemente eden sınıfın tanımını görüyorsunuz. Biz ilgili veritabanının tablolarına <span style="color: #ff9900;">TableFromName</span> ve <span style="color: #ff9900;">TableFromIndex</span> metodları ile erişeceğiz. Bu durumda artık son sınıfımız olan en başta tanımını boş olarak gördüğünüz <span style="color: #ff9900;">SQLServerClass</span> sınıfını yazmaya başlayabiliriz. Ancak bu sınıfımız içinde bir interface tasarlamamız ve ComVisible ile görünür kılmamız gerekiyor. Tanım aşağıdaki gibi olacak;</p>
<pre class="brush: c#">
    [ComVisible(true)]
    public interface ISQLLibrary
    {
        bool Connect(string ServerName, string DatabaseName, string UserName, string Password);
        void Disconnect();

        IDatabaseInterface Database { get; }
    }

    public class SQLServerClass : ISQLLibrary
    {
        private Server srv;
        private IDatabaseInterface CurrentDatabase;

        public IDatabaseInterface Database
        {
            get
            {
                return CurrentDatabase;
            }
        }

        public bool Connect(
                            string ServerName,
                            string DatabaseName,
                            string UserName,
                            string Password
                           )
        {
            bool Result = false;

            srv = new Server(ServerName);
            srv.ConnectionContext.LoginSecure = false;
            srv.ConnectionContext.Login = UserName;
            srv.ConnectionContext.Password = Password;
            Result = true;

            if (Result)
            {
                Database db = srv.Databases[DatabaseName];
                CurrentDatabase = new DatabaseClass(db);
            }

            return Result;
        }

        public void Disconnect()
        {
            srv.ConnectionContext.Disconnect();
        }

        ~SQLServerClass()
        {
            Disconnect();
        }
    }
</pre>
<p> Görüldüğü gibi son derece basit bir yapıya sahip olan SQLServerClass sınıfımız <span style="color: #ff9900;">ISQLLibrary</span> interface&#8217;ini implemente etmiş ve ISQLLibrary ComVisible ile görünür hâle getirilmiş. Kodumuzun nihai hali aşağıdaki gibi olacaktır:</p>
<pre class="brush: c#">
using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Common;
using System.Runtime.InteropServices;
using System.Collections;

namespace SQLClassLibrary
{
    [ComVisible(true)]
    public interface ITableInterface
    {
        DateTime CreateDate { get; }
        string Name { get; }
        long RowCount { get; }
        int ColumnCount { get; }
        int TriggerCount { get; }
    }

    public class TableClass : ITableInterface
    {
        private Table ActiveTable;

        public DateTime CreateDate
        {
            get
            {
                return ActiveTable.CreateDate;
            }
        }
        public string Name
        {
            get
            {
                return ActiveTable.Name;
            }
        }
        public long RowCount
        {
            get
            {
                return ActiveTable.RowCount;
            }
        }
        public int ColumnCount
        {
            get
            {
                return ActiveTable.Columns.Count;
            }
        }
        public int TriggerCount
        {
            get
            {
                return ActiveTable.Triggers.Count;
            }
        }

        public TableClass(Table tbl)
        {
            ActiveTable = tbl;
        }

        ~TableClass()
        {
            // Nesneleri yok etmeyi unutma.! GC ye güvenme..
        }
    }

    [ComVisible(true)]
    public interface IDatabaseInterface
    {
        int ActiveConnections { get; }
        DateTime CreateDate { get; }
        DateTime LastBackupDate { get; }
        string Name { get; }
        double Size { get; }

        int StoredProcedureCount { get; }
        int TableCount { get; }
        int UserDefinedFunctionCount { get; }
        int ViewCount { get; }

        ITableInterface TableFromName(string TableName);
        ITableInterface TableFromIndex(int Index);
    }

    public class DatabaseClass : IDatabaseInterface
    {
        private Database ActiveDatabase;
        private Hashtable hTables;

        public int ActiveConnections
        {
            get
            {
                return ActiveDatabase.ActiveConnections;
            }
        }
        public string Collation
        {
            get
            {
                return ActiveDatabase.Collation;
            }
            set
            {
                ActiveDatabase.Collation = value;
                ActiveDatabase.Alter();
            }
        }
        public DateTime CreateDate
        {
            get
            {
                return ActiveDatabase.CreateDate;
            }
        }
        public DateTime LastBackupDate
        {
            get
            {
                return ActiveDatabase.LastBackupDate;
            }
        }
        public string Name
        {
            get
            {
                return ActiveDatabase.Name;
            }
        }
        public double Size
        {
            get
            {
                return ActiveDatabase.Size;
            }
        }
        public int StoredProcedureCount
        {
            get
            {
                return ActiveDatabase.StoredProcedures.Count;
            }
        }
        public int TableCount
        {
            get
            {
                return ActiveDatabase.Tables.Count;
            }
        }
        public int UserDefinedFunctionCount
        {
            get
            {
                return ActiveDatabase.UserDefinedFunctions.Count;
            }
        }
        public int ViewCount
        {
            get
            {
                return ActiveDatabase.Views.Count;
            }
        }

        public ITableInterface TableFromName(string TableName)
        {
            return (ITableInterface)hTables[TableName];
        }

        public ITableInterface TableFromIndex(int Index)
        {
            ITableInterface Result = null;

            int iCounter = 0;
            IDictionaryEnumerator TableEnum = hTables.GetEnumerator();
            while (TableEnum.MoveNext())
            {
                if (iCounter == Index)
                {
                    Result = (TableClass)TableEnum.Value;
                    break;
                }

                iCounter += 1;
            }

            return Result;
        }

        public DatabaseClass(Database db)
        {
            ActiveDatabase = db;
            hTables = new Hashtable();

            foreach (Table tbl in ActiveDatabase.Tables)
            {
                if (!tbl.IsSystemObject) hTables.Add(tbl.Name, new TableClass(tbl));
            }
        }
    }

    [ComVisible(true)]
    public interface ISQLLibrary
    {
        bool Connect(string ServerName, string DatabaseName, string UserName, string Password);
        void Disconnect();

        IDatabaseInterface Database { get; }
    }

    public class SQLServerClass : ISQLLibrary
    {
        private Server srv;
        private IDatabaseInterface CurrentDatabase;

        public IDatabaseInterface Database
        {
            get
            {
                return CurrentDatabase;
            }
        }

        public bool Connect(
                            string ServerName,
                            string DatabaseName,
                            string UserName,
                            string Password
                           )
        {
            bool Result = false;

            srv = new Server(ServerName);
            srv.ConnectionContext.LoginSecure = false;
            srv.ConnectionContext.Login = UserName;
            srv.ConnectionContext.Password = Password;
            Result = true;

            if (Result)
            {
                Database db = srv.Databases[DatabaseName];
                CurrentDatabase = new DatabaseClass(db);
            }

            return Result;
        }

        public void Disconnect()
        {
            srv.ConnectionContext.Disconnect();
        }

        ~SQLServerClass()
        {
            Disconnect();
        }

    }
}
</pre>
<p>Şimdi Visual Studio ortamında Ctrl+Shift+B tuşlarına basarak yada Build menüsünden Build seçeneklerinden birisini seçerek derleme işlemi yaptığımızda Delphi tarafına geçmeye hazırız demektir. İlgili DLL&#8217;imiz artık projemizi kaydettiğimiz yerde oluşturulduğu gibi, proje ayarlarından Com görünürlüğünü işaretlediğimiz için bu DLL aynı zamanda COM sistemine Register edilmiş durumdadır. Eğer elle register işlemi yapmak istiyorsanız o halde, regasm kullanmak durumunda kalacaksınız. Ancak şu anda bu işi Visual Studio bizim için yapmış durumda. Delphi&#8217;mize geçip yeni bir proje açalım, ve Project/Import Type Library adımından aşağıdaki görselde görebileceğiniz gibi ilgili DLL&#8217;imizi listeden bulalım ve Create Unit&#8217;e basalım.</p>
<p><a href="http://www.tugrulhelvaci.com/wp-content/uploads/sqlclasslibrary_4.png"><img src="http://www.tugrulhelvaci.com/wp-content/uploads/sqlclasslibrary_4.png" alt="sqlclasslibrary_4" title="sqlclasslibrary_4" width="389" height="482" class="aligncenter size-full wp-image-422" /></a></p>
<p><span style="color: #ff9900;">SQLClassLibrary_TLB.pas</span> isimli dosyamız artık oluşmuş durumda. Delphi örneğimiz ise aşağıdaki gibi olacaktır;</p>
<pre class="brush: delphi">
uses SQLClassLibrary_TLB;
..
..
..
procedure TForm1.Button1Click(Sender: TObject);
var
  SqlServer : ISQLLibrary;
begin
  SqlServer := CoSQLServerClass.Create as ISqlLibrary;
  SqlServer.Connect(&#039;TUGRUL&#039;, &#039;BENIMDB&#039;, &#039;sa&#039;, &#039;*****&#039;);
  ShowMessage(
     Format(&#039;Kayıt Sayısı=%d, Kolon Sayısı=%d&#039;,
       [
         SqlServer.Database.TableFromName(&#039;BenimTablom&#039;).RowCount,
         SqlServer.Database.TableFromName(&#039;BenimTablom&#039;).ColumnCount
       ]) );
  SqlServer.Disconnect;
  SqlServer := nil;
end;
</pre>
<p> Özetlemek gerekir ise, Delphi Win32 projelerinde herhangi bir .Net framework kütüphanesine erişebilir ve onu kullanabilirsiniz. Ancak ComVisible görünürlük durumuna dikkat ederek <img src='http://www.tugrulhelvaci.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  Sanırım şimdi makalemin başlarında belirttiğim, CrossTalk ve Delphi Weaver içinde .Net erişimi özelliklerinin ne derece önemli olduğu daha açıktır.</p>
<p>Saygılar, sevgiler..</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tugrulhelvaci.com/?feed=rss2&amp;p=404</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
	</channel>
</rss>
