https://www.buraksenyurt.com/Burak Selim Şenyurt - Parallel Programming2011-12-02T18:51:15+00:00Matematik Mühendisi Bir Bilgisayar Programcısının NotlarıBurak Selim SenyurtBlogEngine.Net Syndication Generatorhttps://www.buraksenyurt.com/opml.axdBurak Selim SenyurtMatematik Mühendisi Bir Bilgisayar Programcısının Notlarıtr-TRBurak Selim Şenyurt0.0000000.000000https://www.buraksenyurt.com/post/Parallel-Programming-ReductionParallel Programming–Reduction2011-12-02T11:51:00+00:00bsenyurt<p><a href="https://www.buraksenyurt.com/pics/wolfenstein.jpg"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: right; padding-top: 0px; border: 0px;" title="wolfenstein" src="/pics/wolfenstein_thumb.jpg" alt="wolfenstein" width="320" height="243" align="right" border="0" /></a>Merhaba Arkadaşlar,</p>
<p>Neredeyse son bir kaç saattir yoğun bir şekilde <strong>Wolfenstein</strong> isimli bilgisayar oyununu oynamaktaydım. Aslında çok fazla bilgisayar oyunu oynayan birisi değilimdir. Hatta bu oyunun ilk versiyonunu çok çok uzun zaman önce oynadığımı ve arada çok az oyunla haşır neşir olduğumu itiraf edebilirim <img class="wlEmoticon wlEmoticon-confusedsmile" style="border-style: none;" src="/pics/wlEmoticon-confusedsmile_12.png" alt="Confused smile" /></p>
<p>Lakin bazen oyun perisi gelip beni bir dürtmekte ve saatlerce bilgisayar başından kalkmadan oyun oynamamı istemekte. Bu gece kendisini kıramadım işte <img class="wlEmoticon wlEmoticon-smile" style="border-style: none;" src="/pics/wlEmoticon-smile_21.png" alt="Smile" /> </p>
<p>Aslında gece boyunca Wolfenstein her ne kadar beni aşırı derece de sürüklemiş olsa da aklımın bir köşesinde beni kemiren paralel programlama konulu düşüncelerimin önüne de geçemedim. Doğruyu söylemek gerekirse şu anda çok geç bir saat de olsa konuyu açıklığa kavuşturmanın ve bununla ilişkili bir blog girdisi üretmenin tam zamanıdır diye düşündüm ve işte karşınızdayım. Oyun perisinin ensesinde biten ilham perisinin isteğiydi bu sanırım. Lafı fazla uzatmadan konuya giriş yapalım dilerseniz.(<em>Bunu belirtmek istedim çünkü bu kısa girişleri sevmeyen bir sürü geliştirici de tanıyorum<img class="wlEmoticon wlEmoticon-smile" style="border-style: none;" src="/pics/wlEmoticon-smile_21.png" alt="Smile" />)</em></p>
<p>Olay paralel programlamada veriyi paralelize etmek ile alakalı. Aslında çok basit ama gözden kaçtığı takdirde önemli hatalara neden olabilecek bir durum söz konusu. Bunu izah etmenin en iyi yolu belkide basit bir örnek üzerinden ilerlemekle olacaktır. Şimdi şöyle düşünelim; elimizde yüsek boyutlu sayısal bir dizi veya koleksiyon olsun ve biz bu sayı kümesi üzerinde örneğin 7 ile tam bölünebilenlerin sayısını bulmak istediğimizi var sayalım. Standart bir <strong>for</strong> döngüsü ile bu işlemi yapabileceğimiz gibi, çok yüksek boyutlu bir sayı olması halinde <strong>Parallel.For</strong> veya <strong>Parallel.ForEach</strong> metodlarını da söz konusu hesaplama için kullanabiliriz pekala <img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_72.png" alt="Winking smile" /> Elimizde çok çekirdekli veya çok işlemcili bir sistem var ise, paralel döngüleri kullanmak pek çok açıdan avantajlı olabilir nitekim. Şimdi az önce bahsetmiş olduğumuz senaryoyu aşağıdaki <strong>Console</strong> uygulamasına ait kod satırlarında simüle ettiğimizi var sayalım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ReductionSample
{
class Program
{
static void Main(string[] args)
{
List<int> numbers = Helper.GetRandomNumberList(9000000);
#region Klasik Yol
int count=0;
for (int i = 0; i < numbers.Count; i++)
{
if (numbers[i] %7==0)
count++;
}
Console.WriteLine("[Klasik For] 7 ile bölünebilen {0} sayı vardır",count.ToString());
#endregion
#region Parallel For
int parallelCount=0;
Parallel.For(0, numbers.Count, (i) =>
{
if (numbers[i] % 7 == 0)
parallelCount++;
}
);
Console.WriteLine("[Parallel.For] 7 ile bölünebilen {0} sayı vardır",parallelCount.ToString());
#endregion
}
}
static class Helper
{
public static List<int> GetRandomNumberList(int NumberCount)
{
List<int> result = new List<int>();
Random rnd = new Random();
for (int i = 0; i < NumberCount; i++)
{
result.Add(rnd.Next(1, 100));
}
return result;
}
}
}</pre>
<p><strong>static </strong>olarak tanımlanmış olan <strong>Helper</strong> sınıfı belirli sayıda rastgele tam sayı üretmek üzere yazılmış <strong>GetRandomNumberList</strong> isimli bir metod içermektedir. Uygulamaya ait <strong>Main</strong> metodu içerisinde ise önce standart bir <strong>for</strong> döngüsü ile ardından da bunun <strong>paralel</strong> versiyonu ile birer iterasyon gerçekleştirilmekte ve<strong> 7 ile bölünebilen sayıların toplam sayısı </strong>hesap edilerek ekrana yazdırılmaktadır.</p>
<p>Aslında sayı aralığı ne kadar yüksek olursa standart <strong>for</strong> döngüsünün hesaplama için daha fazla zaman harcayacağı ve <strong>Parallel.For</strong> döngüsünün aynı işi daha kısa sürede bitireceği aşikardır. Ne varki burada öngöremediğimiz ve belki de tahmin etmediğimiz bir durum daha söz konusudur. Bunu anlamak için kodun çalışma zamanındaki bir kaç çıktısına bakabiliriz <img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_72.png" alt="Winking smile" /></p>
<p><strong>Birinci çalıştırmanın sonuçları;</strong></p>
<p><a href="https://www.buraksenyurt.com/pics/artcl_1_1.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="artcl_1_1" src="/pics/artcl_1_1_thumb.gif" alt="artcl_1_1" width="434" height="126" border="0" /></a></p>
<p>O oooo!!! <img class="wlEmoticon wlEmoticon-surprisedsmile" style="border-style: none;" src="/pics/wlEmoticon-surprisedsmile_1.png" alt="Surprised smile" /></p>
<p><strong>İkinci çalıştırmanın sonuçları;</strong></p>
<p><a href="https://www.buraksenyurt.com/pics/artcl_1_2.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="artcl_1_2" src="/pics/artcl_1_2_thumb.gif" alt="artcl_1_2" width="431" height="126" border="0" /></a></p>
<p>O ooooo too!!! <img class="wlEmoticon wlEmoticon-surprisedsmile" style="border-style: none;" src="/pics/wlEmoticon-surprisedsmile_1.png" alt="Surprised smile" /></p>
<p><strong>Üçüncü çalıştırmanın sonuçları;</strong></p>
<p><a href="https://www.buraksenyurt.com/pics/artcl_1_3.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="artcl_1_3" src="/pics/artcl_1_3_thumb.gif" alt="artcl_1_3" width="432" height="126" border="0" /></a></p>
<p>Şaka mı bu ? <img class="wlEmoticon wlEmoticon-surprisedsmile" style="border-style: none;" src="/pics/wlEmoticon-surprisedsmile_1.png" alt="Surprised smile" /></p>
<p>Dikkat edilecek olursa <strong>Parallel.For</strong> döngüsü 7 ile bölünebilen sayıların miktarını her defasında standart for döngüsüne göre farklı hesaplamış ve aslında hiç birisinde de doğru sonucu tutturamamıştır. Bu aslına bakılacak olursa son derece doğal bir sonuçtur. Nitekim <strong>Parallel.For</strong> döngüsü çalışmaya başladıktan sonra birden fazla <strong>Task</strong> oluşturmakta ve bunları bir veya daha fazla <strong>Thread’</strong> in yönetimine sunmaktadır. Gözden kaçan nokta, bu <strong>Task’</strong> ların her birinin aslında aynı değişken üzerinde hesaplama yapmaya çalışıyor olmalarıdır. Yani, örneğimizde açılan <strong>Task</strong> bloklarının her biri aslında aynı <strong>parallelCount</strong> değişkeni üzerinde bir artım gerçekleştirmeye çalışmaktadır. Bu da çok doğal olarak doğru sonucun <span style="text-decoration: underline;">hesaplanamamasına</span> neden olmaktadır. Peki ya öyleyse çözüm nedir?</p>
<p>Standart bir <strong>for</strong> döngüsünün kullanılması tercih edilebilir <img class="wlEmoticon wlEmoticon-openmouthedsmile" style="border-style: none;" src="/pics/wlEmoticon-openmouthedsmile_19.png" alt="Open-mouthed smile" /> Lakin bu durumda <strong>Parallel</strong> olmanın avantajları kaybedilecektir. Buradaki gibi bir sayı aralığında bu çok önemli gözükmese de, bilimsel veya finansal hesaplama yapan bir uygulamada bu ayrım, performans açısında çok kritik bir fark doğurabilir. Bu nedenle <strong>Parallel</strong> döngüler ile devam edecek isek <strong>Reduction</strong> olarak tanımlanan ve aslında ilerleyen zamanlarda işleyeceğimiz <strong>MapReduce</strong> deseninin temelini oluşturan bir konuyu göz önüne alarak ilerlememiz gerekmektedir. Aslında teorik olarak çok fazla sıkmak istemiyorum sizi, ancak özet olarak işleyişi ifade etmek isterim. <strong>Parallel.For</strong> döngüsünü öyle bir çalıştırmalıyız ki, açılacak olan <strong>Thread’</strong> ler ve bunların içerisinde yer alacak olan <strong>Task’</strong> lar, <strong>parallelCount</strong> sayısının aslında aralarında paylaştıkları bir veri olduğunu bilmelidirdler<img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_72.png" alt="Winking smile" /> Üstelik her bir Thread kendi içerisinde bir toplam sayı hesabı yapmalı ve işleyişini bitirdiğinde de herkes için ortak olan bir değişkene bunu eklemelidir. Bu ekleme işinin ise senkronize edilerek yapılması şarttır.</p>
<p>Neyseki <strong>Parallel.For</strong> ve <strong>Parallel.ForEach</strong> döngüleri bu ortak paylaşımlı veri değişkenlerine izin veren <strong>aşırı yüklenmiş(Overload)</strong> versiyonlara sahiptirler. Çözüm olarak kodumuzu aşağıdaki gibi değiştirmemiz yeterli olacaktır.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">#region Reduction
int reductionCount = 0;
Parallel.For(0, numbers.Count,
() => 0,
(i, state, currentTotal) =>
{
if (numbers[i] % 7 == 0)
currentTotal++;
return currentTotal;
},
(currentTotal) =>
{
Console.WriteLine("{0} Current Total {1}",Thread.CurrentThread.ManagedThreadId,currentTotal.ToString());
Interlocked.Add(ref reductionCount, currentTotal);
}
);
Console.WriteLine("[Reduction] 7 ile bölünebilen {0} sayı vardır",reductionCount.ToString());
#endregion</pre>
<p>Aslında <strong>Parallel.For</strong> için biraz karmaşık bir yazım söz konusu. İlk iki parametre tanıdık. Üçüncü parametrede herhangibir işlem yapmadan geçiyoruz. Önemli olan ise metodun aldığı son iki parametre. Bunlardan ilki <strong>Func<int,ParallelLoopState,TLocal,TLocal></strong> tipinden. Diğeri ise <strong>Action<TLocal></strong> türündendir. Teorik olarak yapılmak istenen her bir <strong>Thread</strong>’ in kendi içerisinde değerlendireceği <strong>özel bir yerel değişken oluşturmak(Private Thread-Local Variable)</strong> ve tüm paralel çalışma tamamlandığında bu özel yerel değişkenlerin bir toplamını hesaplayarak sonuca ulaşmaktır. Bir başka deyişle açılan <strong>Thread</strong>’ ler kendi özel toplam değişkenlerini arttıracaklardır. Bu işlemi,</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">(i, state, currentTotal) =>
{
if (numbers[i] % 7 == 0)
currentTotal++;
return currentTotal;
}</pre>
<p>kısmı gerçekleştirmektedir.</p>
<p>Paralel çalışmaya dahil olan <strong>Thread’</strong> ler işlemlerini bitirdikten sonra da,</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">(currentTotal) =>
{
Console.WriteLine("{0} Current Total {1}",Thread.CurrentThread.ManagedThreadId,currentTotal.ToString());
Interlocked.Add(ref reductionCount, currentTotal);
}</pre>
<p>kodu devreye girmekte ve <strong>Thread’</strong> ler için hesaplanan <strong>currentTotal</strong> değerlerini <strong>ref</strong> ile <strong>reductionCount</strong> değerine eklemektedir. Böylece tüm <strong>Thread’</strong> lerin kendi yerel alanlarındaki veriler üzerinde yaptığı 7 ile bölünebilen sayıların miktarı, birleştirilmektedir. <strong>Interlocked</strong> burada devreye giren önemli bir fonksiyonellik sunmakta ve senkronize bir şekilde currentTotal değerlerinin reductionTotal değişkenine eklenmesine olanak sağlamaktadır. İşte bir koleksiyon içeriğinin bu şekilde tekil bir değere indirgenmesine <strong>Reduction</strong> adı verilmektedir.</p>
<p>Örneğimizi az önceki testte olduğu gibi yine arka arkaya 3 defa çalıştırırsak aşağıdaki ekran görüntüsünde yer alanlara benzer sonuçları elde ettiğimizi görebiliriz.</p>
<p><strong>İlk çalışma sonucu;</strong></p>
<p><a href="https://www.buraksenyurt.com/pics/artcl_1_4.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="artcl_1_4" src="/pics/artcl_1_4_thumb.gif" alt="artcl_1_4" width="439" height="184" border="0" /></a></p>
<p><strong>ikinci çalışma sonucu;</strong></p>
<p><a href="https://www.buraksenyurt.com/pics/artcl_1_5.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="artcl_1_5" src="/pics/artcl_1_5_thumb.gif" alt="artcl_1_5" width="431" height="212" border="0" /></a></p>
<p><strong>üçüncü çalışma sonucu</strong></p>
<p><a href="https://www.buraksenyurt.com/pics/artcl_1_6.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="artcl_1_6" src="/pics/artcl_1_6_thumb.gif" alt="artcl_1_6" width="434" height="220" border="0" /></a></p>
<p><img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_72.png" alt="Winking smile" /></p>
<p>Görüldüğü gibi <strong>Reduction</strong> tekniğine göre yapılan hesaplama sonuçları ile klasik <strong>for</strong> döngüsü ile yapılan hesaplama sonuçları bire bir örtüşmektedir. Elbetteki her çalışma sonrasında <strong>Parallel.For</strong> döngüsünün başlatacağı <strong>Thread’</strong> ler farklı olacaktır. Bu yüzden son <strong>Action</strong> temsilcisinin icrası sonucu üretilen <strong>currentTotal</strong> değerleri farklılıklar gösterecektir. Bu yüzden Current Total değerleri hep farklı sonuçlar vermiştir.</p>
<p>Özetle <strong>Reduction</strong> tekniğini kullandığımızda, paralel olarak işletilen <strong>Thread’</strong> lerin kendi yerel değişkenleri ile çalışmaları sağlanabilmekte ve bunlar sonuç olarak tek bir değişkene indirgenerek bu tip senaryolarda göz önüne alınabilmektedir. Yazımızın başında belirttiğimiz üzere <strong>Reduction</strong> tekniği aslında <strong>MapReduce</strong> deseninin temelini oluşturan önemli bir kavramdır. Bu deseni ilerleyen yazılarımızda sizlere örnek bir senaryo üzerinden aktarmaya çalışıyor olacağım. Eğer bu ve bunun gibi diğer paralel programlama desenlerini merak ediyorsanız <strong>Microsoft’</strong> un ücretsiz olarak indirebileceğiniz <a href="http://www.microsoft.com/download/en/details.aspx?id=19222">Patterns for Parallel Programming : Understanding and Applying Parallel Patterns with .Net Framework 4.0</a> dökümanını okuyabilirsiniz. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2011%2f10%2fReductionSample.rar">ReductionSample.rar (24,88 kb)</a></p>2011-12-02T11:51:00+00:00tplparallel programmingreductionmapreduce patternmap reduceparallel.forparallel.foreachbsenyurtŞimdi şöyle düşünelim; elimizde yüsek boyutlu sayısal bir dizi veya koleksiyon olsun ve biz bu sayı kümesi üzerinde örneğin 7 ile tam bölünebilenlerin sayısını bulmak istediğimizi var sayalım. Standart bir for döngüsü ile bu işlemi yapabileceğimiz gibi, çok yüksek boyutlu bir sayı olması halinde Parallel.For veya Parallel.ForEach metodlarını da söz konusu hesaplama için kullanabiliriz pekala...https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=23f00892-dda7-4566-9faa-e76971552fe00https://www.buraksenyurt.com/trackback.axd?id=23f00892-dda7-4566-9faa-e76971552fe0https://www.buraksenyurt.com/post/Parallel-Programming-Reduction#commenthttps://www.buraksenyurt.com/syndication.axd?post=23f00892-dda7-4566-9faa-e76971552fe0https://www.buraksenyurt.com/post/Task-Relations-ve-Continuation-MetodlariTask Relations–Continuation Metodları2011-11-18T12:00:00+00:00bsenyurt<p>Merhaba Arkadaşlar,</p>
<p><a href="https://www.buraksenyurt.com/pics/1342533_gray_day_over_water.jpg"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: right; padding-top: 0px; border: 0px;" title="1342533_gray_day_over_water" src="/pics/1342533_gray_day_over_water_thumb.jpg" alt="1342533_gray_day_over_water" width="300" height="200" align="right" border="0" /></a>Böylesine yağmurlu ve sabah trafiğinin tavan yaptığı bir günde size ne Radyo Eksen’ deki güzel melodiler, ne de okuduğunuz mizah dergisindeki karikatürler iyi gelmiyorsa, başka bir şeyle uğraşmanın yeridir diyebilirim. Ben bu sıkıntıyı aşmak ve kendimi daha iyi hissetmek adına bir makale daha yazmaya karar verdim ve hemen <strong>Windows Live Writer</strong> programını açtım <img class="wlEmoticon wlEmoticon-smile" style="border-style: none;" src="/pics/wlEmoticon-smile_20.png" alt="Gülümseme" /> Yanında da <strong>Paint.Net’</strong> i. Bakalım bu gün menümüzde neler var?<em>(Gerçi şöyle sahil kenarına gidip yürüyüşte yapabilirdim ama bu seferlik böyle olsun)</em></p>
<p><strong>Microsoft</strong>, paralel programlama ile ilişkili olarak olabildiğince çok alternatifi düşünmüş ve kullanıma sunmuştur. Özellikle senaryo bazında düşündüğümüzde paralel çalışma algoritmalarından senkronizasyona kadar pek çok noktada bu durumu görmekteyiz. Söz gelimi <strong>Task’</strong> <strong>ler arası ilişkiler(Relations)</strong> ve veri transferleri konusunda olabilecek tüm kombinasyonlar değerlendirilmeye çalışılmıştır. <strong>Task’</strong> ler arası ilişkiler oldukça ilginç ve enteresan bir konudur. Gerçek hayat senaryolarında doğru ve uygun karşılıklarını bulmak her ne kadar zor olsa da en azından teorik olarak paralel programlamacıların konuyu bilmesi önemlidir. İşte bu yazımızda <strong>Task</strong> örnekleri arası ilişkiler konusuna değinmeye başlayacak ve ilk olarak <strong>Continuations</strong> seçeneklerini irdelemeye çalışacağız.</p>
<p><strong>Continuations</strong> tekniklerinde, bir <strong>Task’</strong> ın çalıştırılması veya icra edilmesi, atası olan ya da öncesinden tanımlanıp kendisine bağlanan <strong>Task</strong> örneklerine bağlıdır. Normal şartlar altında size tek bir <strong>Task</strong> örneği ve bu <strong>Task</strong> çalışmasını bitirdikten sonra devreye girmesi gereken bir <strong>Task</strong> örneğinin ele alındığı senaryoyu aktarmam gerekiyor. Ancak bana göre konunun daha iyi anlaşılabilmesi için aşağıdaki kod parçasında yer alan örneği göz önüne alarak başlamamız daha doğru olacaktır <img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_71.png" alt="Göz kırpan gülümseme" /></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskContinuation
{
class Program
{
static void Main(string[] args)
{
#region Task Örnekleri
Task<string> TaskA = new Task<string>(() =>
{
Thread.SpinWait(Int32.MaxValue-4000000);
Console.WriteLine("Task A");
return "Dennis Ritchie";
}
);
Task<int> TaskB = new Task<int>(() =>
{
Thread.SpinWait(Int32.MaxValue - 5000000);
Console.WriteLine("Task B");
return 10;
}
);
Task<bool> TaskC = new Task<bool>(() =>
{
Thread.SpinWait(Int32.MaxValue - 10000000);
Console.WriteLine("Task C");
return true;
}
);
#endregion
Task[] tasks={TaskA,TaskB,TaskC};
// Succesor Task, TaskA,TaskB ve TaskC tamamlanıncaya kadar bekleyecektir
Task succesorTask = Task.Factory.ContinueWhenAll(tasks, (antecedentTasks) =>
{
Console.WriteLine("Succesor Task");
Console.WriteLine("{0}\n{1}\n{2}",TaskA.Result,TaskB.Result,TaskC.Result);
}
);
TaskA.Start();
TaskB.Start();
TaskC.Start();
Task.WaitAll(tasks); // TaskA, B ve C' nin tamamlanmasını bekle
succesorTask.Wait(); // succesorTask' ın tamamlanmasını bekle
Console.WriteLine("Program Sonu");
Console.ReadLine();
}
}
}</pre>
<p>Öncelikli olarak kod parçamızı kısaca inceleyelim. Senaryomuzda 4 adet <strong>Task</strong> örneği bulunmaktadır. <strong>TaskA, TaskB </strong>ve <strong>TaskC</strong> isimli <strong>Task</strong> nesne örnekleri geriye farklı tiplerde değerler döndürmektedir. Dikkat edilmesi gereken nokta <strong>succesorTask</strong> değişkeni ile tanımlanmış olan <strong>Task</strong> örneğinin oluşturulma şeklidir. Dikkat edileceği üzere <strong>Task.Factory.ContinueWhenAll</strong> metodu kullanılmıştır. Buna göre, <strong>ContinueWhenAll</strong> metodunun ilk parametresine verilen <strong>Task</strong> dizisine ait <strong>Task</strong> örnekleri tamamlanmadığı sürece, ikinci parametre ardından gelen <strong>Anonymous Metod</strong> içeriği çalıştırılmayacaktır <img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_71.png" alt="Göz kırpan gülümseme" /> Bir başka deyişle<strong> Succesor Task</strong> devreye girmeyecektir. Bu durumu uygulamayı çalışma zamanında <strong>Debug</strong> ederken daha net bir şekilde görebiliriz.</p>
<p><a href="https://www.buraksenyurt.com/pics/bei_33.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="bei_33" src="/pics/bei_33_thumb.gif" alt="bei_33" width="524" height="585" border="0" /></a></p>
<p>Şekilden de görüleceği üzere <strong>Task</strong> örneklerinin her üçü de <strong>Start</strong> edilmiş ancak <strong>succesorTask’</strong> ın o anki <strong>Status</strong> durumu <strong>WaitingForActivation</strong> olarak kalmıştır. Bunun sebebi, önceki <strong>Task</strong> örneklerinin tamamının işleyişini henüz bitirmemiş olmasıdır. Örnek uygulamamızın çalışma zamanındaki görüntüsü ise aşağıdaki gibi olacaktır.</p>
<p><a href="https://www.buraksenyurt.com/pics/bei_34.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="bei_34" src="/pics/bei_34_thumb.gif" alt="bei_34" width="200" height="142" border="0" /></a></p>
<p>Görüldüğü üzere bir <strong>Task</strong> örneğinin, kendisinden önceki başka <strong>Task</strong> örneklerinin tamamının işleyişini bitirmesinden sonra devreye girmesi bekleniyorsa, <strong>ContinueWhenAll</strong> metodu kullanılabilir. Aslında bakarsanız daha gerçekçi senaryolara gitmek için <strong>Continue</strong>… metodlarının aldığı <strong>TaskContinuationOptions</strong> <strong>enum</strong> sabitinin değerlerine bakmakta yarar vardır. Çünkü bu <strong>Enum</strong> sabitinin değerleri,<strong> Succesor Task</strong> örneğinin hangi durumlarda devreye girmesi konusunda daha farklı bakış açılarının değerlendirilebilmesini sağlamaktadır. Enum sabitinin alabileceği değerler ise şunlardır.</p>
<ul>
<li>None</li>
<li>AttachedToParent</li>
<li>ExecuteSynchronously</li>
<li>LongRunning</li>
<li>NotOnCanceled</li>
<li>NotOnFaulted</li>
<li>NotOnRanToCompletion</li>
<li>OnlyOnCanceled</li>
<li>OnlyOnFaulted</li>
<li>OnlyOnRanToCompletion</li>
<li>PreferFairness</li>
</ul>
<p>Konuyu daha net kavramak adına örnek uygulamamızdaki kodlarımızı biraz değiştirelim.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
namespace TaskContinuation
{
class Program
{
static void Main(string[] args)
{
#region Senaryo #2
Task TaskA = new Task(() =>
{
Console.WriteLine("Adventure Servisi üzerinden işlemler");
throw new FileNotFoundException();
}
);
Task succesorTask = TaskA.ContinueWith(
(antecedentTasks) =>
{
Console.WriteLine("Bir hata oluştu. Rollback operasyonu yapılacak");
}
, TaskContinuationOptions.OnlyOnFaulted
);
TaskA.Start();
try
{
TaskA.Wait();
}
catch(AggregateException excp)
{
succesorTask.Wait();
}
#endregion
Console.WriteLine("Program Sonu");
Console.ReadLine();
}
}
}</pre>
<p>Şimdi bu senaryoda daha farklı bir durum söz konusudur. <strong>TaskA</strong> içerisinde bilinçli olarak bir <strong>Exception</strong> üretildiği görülmektedir. Tabiki gerçek hayat senaryosunda böyle bir olasık olma ihtimali olduğu göz önüne alınmalıdır. Diğer yandan <strong>TaskA</strong> üzerinden <strong>ContinueWith</strong> metodunu kullanarak <strong>succesorTask</strong> örneği oluşturulmakta ve <strong>OnlyOnFaulted</strong> <strong>enum sabiti</strong> değeri verilmektedir. Buna göre, <strong>succesorTask</strong> nesne örneğinin devreye girme durumu, bir önceki <strong>Antecedent Task</strong> örneği içerisinde bir <strong>Exception</strong> oluşması ve <strong>Faulted</strong> durumuna düşmesi halidir. Dolayısıyla örneğimizi çalıştırdığımızda aşağıdaki ekran görüntüsüne benzer bir sonuç ile karşılaşmamız son derece doğaldır.</p>
<p><a href="https://www.buraksenyurt.com/pics/bei_35.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="bei_35" src="/pics/bei_35_thumb.gif" alt="bei_35" width="477" height="131" border="0" /></a></p>
<p>Diğer yandan <strong>throw Exception</strong> satırı yorum haline getirilir veya kaldırılırsa bu kez çalışma zamanı görüntüsü aşağıdaki gibi olacaktır.</p>
<p><a href="https://www.buraksenyurt.com/pics/bei_36.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="bei_36" src="/pics/bei_36_thumb.gif" alt="bei_36" width="306" height="74" border="0" /></a></p>
<p>Görüldüğü üzere bir önceki <strong>Task</strong> örneğinde herhangibir <strong>Exception</strong> durumu söz konusu olmadığından, <strong>succesorTask</strong> örneğine ait metod icra edilmemiştir. Tabi burada akıllı bir geliştirici hemen şunu soracaktır; <strong>Birden fazla Task örneğinden herhangibirinde bir hata meydana geldiğinde ilgili Succesor Task devreye girse olmaz mı? </strong><img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_71.png" alt="Göz kırpan gülümseme" /> Güzel soru…Bu vakayı test etmek için aşağıdaki kod parçasını deneyebiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.IO;
using System.Threading.Tasks;
namespace TaskContinuation
{
class Program
{
static void Main(string[] args)
{
#region Senaryo #2
Task TaskA = new Task(() =>
{
Console.WriteLine("Adventure Servisi üzerinden işlemler");
//throw new FileNotFoundException();
}
);
Task TaskB = new Task(() =>
{
Console.WriteLine("Northwind Servisi üzerinden işlemler");
//throw new FileNotFoundException();
}
);
Task[] tasks = { TaskA, TaskB };
Task succesorTask = Task.Factory.ContinueWhenAny(tasks,
(antecedentTasks) =>
{
Console.WriteLine("Bir hata oluştu. Rollback operasyonu yapılacak");
}
, TaskContinuationOptions.OnlyOnFaulted
);
TaskA.Start();
TaskB.Start();
try
{
Task.WaitAll(tasks);
}
catch(AggregateException excp)
{
succesorTask.Wait();
}
#endregion
Console.WriteLine("Program Sonu");
Console.ReadLine();
}
}
}</pre>
<p>Ne yazık ki uygulamayı çalıştırdığımızda aşağıdaki ekran görüntüsünde yer alan <strong>Exception</strong> mesajını alırız <img class="wlEmoticon wlEmoticon-sadsmile" style="border-style: none;" src="/pics/wlEmoticon-sadsmile_8.png" alt="Üzgün gülümseme" /></p>
<p><a href="https://www.buraksenyurt.com/pics/bei_37.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="bei_37" src="/pics/bei_37_thumb.gif" alt="bei_37" width="530" height="468" border="0" /></a></p>
<p>Bu aslında <strong>TaskContinuationOptions</strong> <strong>enum sabitine</strong> verdiğimiz <strong>OnlyOnFaulted</strong> değeri için söz konusu bir durumdur. <em>(Aslına bakarsanız ben bu senaryonun çalışmasını beklerdim <img class="wlEmoticon wlEmoticon-confusedsmile" style="border-style: none;" src="/pics/wlEmoticon-confusedsmile_11.png" alt="Kafası karışmış gülümseme" />)</em> Diğer <strong>enum</strong> sabiti değerlerinde bu tip bir sorun ile karşılaşmasanız da <strong>OnlyOnFaulted</strong> hakkatten bir <strong>Fault</strong> vermektedir <img class="wlEmoticon wlEmoticon-openmouthedsmile" style="border-style: none;" src="/pics/wlEmoticon-openmouthedsmile_18.png" alt="Açık ağızlı gülümseme" /></p>
<p>Yazımızın buraya kadarki kısmında kısaca <strong>Task</strong> örnekleri arasındaki ilişkileri sağlamak adına <strong>Continous</strong> tekniklerini ve özellikle <strong>ContinueWhenAll ve</strong> <strong>ContinueWith</strong> metodlarını irdeledik. Bu iki metoda ek olarak <strong>ContinueWhenAny</strong> isimli bir metodun daha olduğunu belirtmek isterim. Bu metod aslında bir <strong>Task</strong> dizisi içerisindeki <strong>Task’</strong> lerden herhangibiri tamamlandıktan sonra <strong>Succesor</strong> <strong>Task</strong> örneğinin devreye girmesi amacıyla tasarlanmıştır. Bu durumu analiz etmek için aşağıdaki kod parçasını göz önüne alabiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskContinuation
{
class Program
{
static void Main(string[] args)
{
#region Senaryo #3
Task TaskA = new Task(() =>
{
Thread.Sleep(4000);
Console.WriteLine("Adventure Servisi üzerinden işlemler");
//throw new FileNotFoundException();
}
);
Task TaskB = new Task(() =>
{
Thread.Sleep(2000);
Console.WriteLine("Northwind Servisi üzerinden işlemler");
//throw new FileNotFoundException();
}
);
Task[] tasks = { TaskA, TaskB };
Task succesorTask = Task.Factory.ContinueWhenAny(tasks,
(antecedentTasks) =>
{
Console.WriteLine("Succesor Task");
}
);
TaskA.Start();
TaskB.Start();
try
{
Task.WaitAll(tasks);
}
catch (AggregateException excp)
{
}
succesorTask.Wait();
#endregion
Console.WriteLine("Program Sonu");
Console.ReadLine();
}
}
}</pre>
<p>Uygulama kodunda yer alan <strong>TaskA</strong> ve <strong>TaskB</strong> nesne örneklerine ait kod bloklarından ilk olarak <strong>TaskB</strong> tamamlanacaktır<em>(Verilen Thread durdurma süreleri gereği)</em> Buna göre de çalışma zamanında TaskB tamamlanır tamamlanmaz <strong>Succesor Task</strong> bloğu yürütülecektir. Aşağıda görüldüğü gibi.</p>
<p><a href="https://www.buraksenyurt.com/pics/bei_38.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="bei_38" src="/pics/bei_38_thumb.gif" alt="bei_38" width="316" height="106" border="0" /></a></p>
<p>Böylece geldik bir yazımızın daha sonra <img class="wlEmoticon wlEmoticon-smile" style="border-style: none;" src="/pics/wlEmoticon-smile_20.png" alt="Gülümseme" /> Bir sonraki yazıda büyük bir olasılıkla <strong>Task’</strong> ler arası ilişkiler konusuna devam ediyor olacağım. Ama araya başka bir konu da girebilir. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2011%2f10%2fTaskContinuation.rar">TaskContinuation.rar (22,47 kb)</a></p>2011-11-18T12:00:00+00:00tpltask parallel librarytask relationsparallel programmingbsenyurtContinuations tekniklerinde, bir Task’ ın çalıştırılması veya icra edilmesi, atası olan ya da öncesinden tanımlanıp kendisine bağlanan Task örneklerine bağlıdır. Normal şartlar altında size tek bir Task örneği ve bu Task çalışmasını bitirdikten sonra devreye girmesi gereken bir Task örneğinin ele alındığı senaryoyu aktarmam gerekiyor. Ancak bana göre konunun daha iyi anlaşılabilmesi için aşağıdaki kod parçasında yer alan örneği göz önüne alarak başlamamız daha doğru olacaktırhttps://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=17c2b913-0b97-4fb1-bfe1-6b6d5cffc3250https://www.buraksenyurt.com/trackback.axd?id=17c2b913-0b97-4fb1-bfe1-6b6d5cffc325https://www.buraksenyurt.com/post/Task-Relations-ve-Continuation-Metodlari#commenthttps://www.buraksenyurt.com/syndication.axd?post=17c2b913-0b97-4fb1-bfe1-6b6d5cffc325https://www.buraksenyurt.com/post/Barrier-ClassBarrier Class, Sıralama Algoritmaları ve At Yarışı2011-10-17T09:47:00+00:00bsenyurt<p><a href="https://www.buraksenyurt.com/pics/The%20starting%20gate.jpg"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: right; padding-top: 0px; border: 0px;" title="The starting gate" src="/pics/The%20starting%20gate_thumb.jpg" alt="The starting gate" width="240" height="160" align="right" border="0" /></a>Merhaba Arkadaşlar,</p>
<p>At yarışlarına pek ilgim yoktur aslında ama tam da bu günlerde okuduğum kitap nedeniyle, paralel programlama ile aralarında sıkı bir ilişki olduğunu ifade edebilirim <img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_70.png" alt="Göz kırpan gülümseme" /> Bildiğiniz üzere bu yarışların pek çok meraklısı bulunmaktadır. Özellikle yarışları stadyumdan seyredenler oldukça heyecanlıdır. Gerçi yarışın başlamasından önceki tahminler tam başlangıç anında yerini endişeye bırakır. Bizim gibi yazılımcılar için önemli olan ise başlangıç anıdır. Neden mi? Bazen bir yarışa başlanırken, yarışa iştirak eden katılımcıların start düzlüğünde bir arada yer almalarını bekleriz. At yarışlarındaki başlangıç kapıları bu işe yaramaktadır. Tahmin edeceğiniz gibi bu kapılar atların başlangıç işareti gelmeden hareket etmelerini engellemek üzere tasarlanmışlardır.</p>
<p>Çok doğal olarak ve pek tabi ki aynı durum program ortamı içerisinde yer alan ve paralel çalıştırılması düşünülen metodlar içinde geçerli olabilir<img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_70.png" alt="Göz kırpan gülümseme" /> Bir başka deyişle bazı senaryolarda paralel olarak yürütülmesi istenen metodların, çalışmaya başlamadan önce bir kutu içerisinde de paralel olarak dizilmeleri ve kutu beklenen limiti aştığında başlatılmaları istenebilir. Yani paralel çalıştırılması istenen fonksiyonellikler hazırlanıp belirli bir başlangıç noktasına doğru senkronize edilir ve sonrasında belirli bir kurala göre<em>(örneğin kutunun limitini doldurması gibi)</em> başlatılırlar.</p>
<p>Durumu daha iyi analiz edebilmek için dilerseniz basit bir senaryo üzerinden ilerlemeye çalışalım. Malumunuz günümüz üniversitelerinde özellikle algoritma derslerinde en çok sorulan, okutulan, ezberletilen konuların başında sıralama teknikleri gelmektedir. <strong>Bubble Sort, Quick Sort, Insertion Sort</strong> vb…Eminim çoğumuz bunları ezberlemek ve sınavlarda çıkacak diye uzun geceler boyu hazırlanmak zorunda kalmışızdır <img class="wlEmoticon wlEmoticon-confusedsmile" style="border-style: none;" src="/pics/wlEmoticon-confusedsmile_10.png" alt="Kafası karışmış gülümseme" /> Peki ya bu sıralama algoritmalarını bir yarışa sokmak ister miydiniz? Aslında her birini bir at olarak düşünüp aynı anda yarışı başlatmak istemez miydiniz dersem çok daha uygun olacaktır <img class="wlEmoticon wlEmoticon-smile" style="border-style: none;" src="/pics/wlEmoticon-smile_19.png" alt="Gülümseme" /> Bunu gerçekleştirirken aslında paralel programlama tekniklerinden yararlanma şansımız bulunmaktadır. Her bir sıralama algoritması için birer <strong>Task</strong> nesne örneği üretip bunları aynı anda çalıştırmamız yeterli olacaktır…Aynı anda <img class="wlEmoticon wlEmoticon-openmouthedsmile" style="border-style: none;" src="/pics/wlEmoticon-openmouthedsmile_17.png" alt="Açık ağızlı gülümseme" /> </p>
<p>İşte <strong>Barrier</strong> sınıfını kullanmak için güzel bir fırsat. Yazımızın odak noktası hangi sıralama algoritmasının hızlı çalıştığını tespit etmekten ziyade, <strong>Barrier</strong> sınıfını kullanarak ilgili sıralama algoritma metodlarını aynı anda başlatmaktır. Bu amaçla ben örnek olarak 3 adet sıralama algoritmasını baz almayı uygun gördüm. <strong>Bubble Sort, Quick Sort</strong> ve<strong> Insertion Sort…</strong></p>
<p>Hedefimiz bu algoritmaları içeren metodları birer <strong>Task</strong> olarak tanımlamak ve <strong>start</strong> düzlüğünde aynı kapı içerisine yerleştirerek aynı anda çalıştırılmalarını sağlamaktır. Kabaca <strong>Barrier</strong> sınıfını kullanarak gerçekleştireceğimiz örneğimizin çalışma zamanındaki akışı belkide aşağıdaki grafik ile ifade edilebilir.</p>
<p><a href="https://www.buraksenyurt.com/pics/bei_30.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="bei_30" src="/pics/bei_30_thumb.gif" alt="bei_30" width="428" height="490" border="0" /></a></p>
<p><strong>Barrier</strong> sınıfı öncellikli olarak bir kapasite bildirimi ile çalışmaktadır. Bu kapasite aslında kutu içerisine dahil edilecek olan <strong>Task</strong> sayısını bildirmektedir. Söz konusu <strong>Task</strong> sayısına ulaşıldığında ilgili metodların aynı anda başlatılması söz konusu olacaktır<em>(Tabi arkadan gelen başka bir Task daha olursa, kapıdaki örnekler bir anda çıkana kadar beklemek zorunda kalacaktır)</em> Durumu elbetteki örnek kod parçası üzerinden anlamamız daha uygundur. Bu amaçla basit bir <strong>Console</strong> uygulaması açıp aşağıdaki kod içeriğini yazarak ilerleyebiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace SortingTest
{
class Program
{
static string insertionSortStartTime=String.Empty;
static string quickSortStartTime = String.Empty;
static string bubbleSortStartTime = String.Empty;
static void Main(string[] args)
{
// Önce rastgele sayı koleksiyonumuz oluşturuluyor
List<int> testCollection = CreateRandomNumbers(100000,1, 100000);
#region Barrier Kullanımı
// 3 metodluk bir başlangıç kutusu tanımlıyoruz.
Barrier _barrier = new Barrier(3);
// Bubble Sort metodu için bir Task üretiliyor ve Task ilk olarak Barrier içerisine ilave ediliyor
Task bubbleSortingTask = new Task(
() =>
{
_barrier.SignalAndWait();
BubbleSorting(testCollection);
}
);
bubbleSortingTask.Start();
// Quick Sort metodu için bir Task üretiliyor ve Task içerisinde yine Barrier' e bir ekleme işlemi gerçekleştiriliyor
Task quickSortingTask = new Task(
() =>
{
_barrier.SignalAndWait();
QuickSorting(testCollection, 0, testCollection.Count - 1);
}
);
quickSortingTask.Start();
// Insertion Sort metodu için bir Task üretiliyor ve Barrier içerisine ekleniyor
Task insertionSortingTask = new Task(
() =>
{
_barrier.SignalAndWait();
InsertionSorting(testCollection);
}
);
insertionSortingTask.Start();
// Task' lerin tamamlanması uzun sürebilir o yüzden bekleyelim
Task[] tasks = { bubbleSortingTask, quickSortingTask, insertionSortingTask };
Task.WaitAll(tasks);
Console.WriteLine("Bubble : {0}\nQuick : {1}\nInsertion : {2}",bubbleSortStartTime,quickSortStartTime,insertionSortStartTime);
#endregion
#region Sequential çalıştırma
bubbleSortStartTime = String.Empty;
quickSortStartTime = String.Empty;
insertionSortStartTime = String.Empty;
Console.WriteLine("\nSequential Çalıştırma\n");
// Bubble algoritmasına göre sıralama
var bubbleOrdered = BubbleSorting(testCollection);
// Quick sort algoritmasına göre sıralama
var quickOrdered = QuickSorting(testCollection, 0, testCollection.Count - 1);
// Insertion sort algoritmasına göre sıralama
var insertionOrdered = InsertionSorting(testCollection);
Console.WriteLine("Bubble : {0}\nQuick : {1}\nInsertion : {2}", bubbleSortStartTime, quickSortStartTime, insertionSortStartTime);
#endregion
}
/// <summary>
/// Bubble sıralama algoritmasına göre sayı dizisini sıralar
/// </summary>
/// <param name="Numbers">Sıralanacak olan sayı koleksiyonu</param>
/// <returns>Parametre olarak gelen Numbers koleksiyonunun sıralanmış halidir</returns>
static List<int> BubbleSorting(List<int> Numbers)
{
if (String.IsNullOrEmpty(bubbleSortStartTime))
bubbleSortStartTime = DateTime.Now.ToLongTimeString();
for (int i = 0; i < Numbers.Count - 1; i++)
{
for (int j = 1; j < Numbers.Count - i; j++)
{
if (Numbers[j] < Numbers[j - 1])
{
int temporary = Numbers[j - 1];
Numbers[j - 1] = Numbers[j];
Numbers[j] = temporary;
}
}
}
return Numbers;
}
/// <summary>
/// QuickSort veya Pivot Sort algoritmasına göre sıralama işlemini yapan metoddur.
/// </summary>
/// <param name="Numbers">Sıralanacak olan sayı koleksiyonu</param>
/// <param name="LeftValue">Sol değer</param>
/// <param name="RightValue">Sağ değer</param>
/// <returns>Sayı koleksiyonunun sıralanmış hali</returns>
static List<int> QuickSorting(List<int> Numbers, int LeftValue, int RightValue)
{
if (String.IsNullOrEmpty(quickSortStartTime))
quickSortStartTime = DateTime.Now.ToLongTimeString();
int i = LeftValue;
int j = RightValue;
double pivotValue = ((LeftValue + RightValue) / 2);
int x = Numbers[Convert.ToInt32(pivotValue)];
int w = 0;
while (i <= j)
{
while (Numbers[i] < x)
{
i++;
}
while (x < Numbers[j])
{
j--;
}
if (i <= j)
{
w = Numbers[i];
Numbers[i++] = Numbers[j];
Numbers[j--] = w;
}
}
if (LeftValue < j)
{
QuickSorting(Numbers, LeftValue, j);
}
if (i < RightValue)
{
QuickSorting(Numbers, i, RightValue);
}
return Numbers;
}
/// <summary>
/// InsertionSort algoritmasına göre sayı koleksiyonunu sıralar
/// </summary>
/// <param name="Numbers">Sıralanacak olan sayı koleksiyonu</param>
/// <returns>Sayı koleksiyonunun sıralanmış hali</returns>
static List<int> InsertionSorting(List<int> Numbers)
{
if (String.IsNullOrEmpty(insertionSortStartTime))
insertionSortStartTime = DateTime.Now.ToLongTimeString();
for (int j = 1; j < Numbers.Count; j++)
{
int keyValue = Numbers[j];
int i = j - 1;
while (i >= 0 && Numbers[i] > keyValue)
{
Numbers[i + 1] = Numbers[i];
i = i - 1;
}
Numbers[i + 1] = keyValue;
}
return Numbers;
}
/// <summary>
/// Sıralama testlerine tabi tutulacak sayı koleksiyonunu üretir
/// </summary>
/// <param name="NumberCount">Kaç adet sayıdan oluşacak</param>
/// <param name="StartValue">Rastgele sayı üretici için minimum değer</param>
/// <param name="EndValue">Rastgele sayı üretici için maksimum değer</param>
/// <returns>Oluşan sayı koleksiyonu</returns>
static List<int> CreateRandomNumbers(int NumberCount,int StartValue, int EndValue)
{
List<int> collection = new List<int>();
Random rnd = new Random();
for (int i = 0; i < NumberCount; i++)
{
collection.Add(rnd.Next(StartValue,EndValue));
}
return collection;
}
}
}</pre>
<p>Aslında uygulamamız çok karmaşık gözükse de oldukça basit temellere dayanıyor. Sıralama algoritmalarının üçü için ve bir de rastgele sayı üretimi için tasarlanmış metodlarımız bulunmaktadır. Asıl önemli olan kısım ise <strong>Main</strong> metodu içerisinde yaptıklarımızdır. Dikkat edileceği üzere ilk olarak <strong>Barrier</strong> nesne örneği kullanılarak bir akış gerçekleştirilmektedir.</p>
<p><strong>Barrier</strong> sınıfına ait nesne örneği üretildikten sonra, ilgili <strong>Task</strong> örneklerinin buraya dail olmaları için, <strong>anonymous</strong> metodlarda <strong>SignalAndWait</strong> fonksiyonuna birer çağrıda bulunulduğuna dikkat edelim <img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_70.png" alt="Göz kırpan gülümseme" /> Sonrasında <strong>Task</strong> nesne örneklerinin <strong>Start</strong> metodları çağırılıyor. Ne var ki <strong>Barrier</strong> bloğunun kapasitesi dolana kadar ilgili metodlar yürütülmeyecektir. Zaten söz konusu <strong>Task</strong> örneklerinin <strong>Barrier</strong> bloğuna dahil edilme sebebi de kapasite dolumundan sonra aynı anda çalışmaya başlamalarının istenmesidir. Uygulamayı çalıştırdığımızda aşağıdaki ekran görüntüsündekine benzer sonuçlar aldığımızı görebiliriz.</p>
<p><a href="https://www.buraksenyurt.com/pics/bei_31.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="bei_31" src="/pics/bei_31_thumb.gif" alt="bei_31" width="371" height="171" border="0" /></a></p>
<p>Görüldüğü üzere <strong>Barrier</strong> kullanımı nedeni ile ilk üç <strong>Task</strong> örneğinin tam olarak aynı anda başlatılmaları söz konusudur<em>(Aslında bakarsanız milisaniye cinsinden de kontrol edilmeleri gerekmektedir)</em> Diğer yandan <strong>Sequential</strong> çalışma da elbetteki metodlar başlatıldıkları sıra ile yürütülmektedirler. Burada <strong>Quick Sort</strong> algoritmasının gerçekten çok hızlı olması nedeni ile işini çok kısa sürede bitirdiğini ve hemen <strong>Insertion Sort</strong> metoduna geçilebildiğini vurgulamak isterim.</p>
<p>Sanırım <strong>Barrier</strong> tipinin kullanımını biraz olsun kavrayabilmişizdir. Konuyu daha net kavrayabilmek için uygulamayı <strong>Debug</strong> modda çalıştırmanızı öneririm. Özellikle <strong>Sort</strong> metodlarının başlangıç noktalarına <strong>Breakpoint</strong> koyarsanız, <strong>Main</strong> metodu içerisinde ilgili <strong>Task</strong> nesne örnekleri üzerinden <strong>Start</strong> metodları çağırılsa bile <strong>Barrier</strong> bloğunun kapasitesi dolana kadar bu <strong>breakpoint</strong> noktalarına gelinemediğini görüyor olacaksınız. Burada eğer <strong>Debug</strong> <strong>Mod</strong>’ da <strong>Parallel</strong> <strong>Tasks</strong> penceresine bakarsanız <strong>Task</strong> örneklerinin <strong>Start</strong> metodlarına yapılan çağrılardan sonra <strong>Status</strong> olarak <strong>Running’ </strong>e geçtikleri görülecektir ki bu sizi yanıltmamalıdır. Bu sebepten örneği <strong>Debug</strong> modda izlemeniz son derece önemlidir <img class="wlEmoticon wlEmoticon-winkingsmile" style="border-style: none;" src="/pics/wlEmoticon-winkingsmile_70.png" alt="Göz kırpan gülümseme" /> </p>
<p>Bakın ben çalışma zamanında durumu analiz ettiğimde <strong>Barrier</strong> bölgesine son eklenen <strong>Insertion Sort</strong> algoritma metodundan sonra, bu son eklenen ilk olmak üzere işleyişin başlayabildiğin gördüm. Bir başka deyişle <strong>Barrier</strong> için belirtilen 3 kapasite değeri <strong>InsertionSort</strong> metodu eklenene kadar dolmadığından hiç bir metod başlatılmamıştır.</p>
<p><a href="https://www.buraksenyurt.com/pics/bei_32.gif"><img style="background-image: none; margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="bei_32" src="/pics/bei_32_thumb.gif" alt="bei_32" width="587" height="476" border="0" /></a></p>
<p><a href="https://www.buraksenyurt.com/pics/240496b.jpg"><img style="margin: 4px 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px; float: right; background-image: none;" title="240496b" src="/pics/240496b_thumb.jpg" alt="240496b" width="179" height="216" align="left" border="0" /></a>Parallel programlama ile ilişkili olarak bir kaç yazı yazmayı daha planlamaktayım. İçeride gözden kaçan oldukça önemli ve hayati pek çok konu var. Özellikle de gelecek nesil .Net platformunda bu konunun ne kadar önemli olduğunu düşünürsek geliştirici olarak iyi hazırlanmamız gerektiği kanısındayım.</p>
<p>Aslında ben şu sıralarda <a href="http://amzn.com/0735640602">Parallel Programming Step by Step</a> isimli kitabı takip ediyorum. İnce bir kitap ve sizlere de şiddetle tavsiye ederim. Gerçi ince olduğuna aldanmayın lütfen. Yeteri kadar doyurucu bilgi ve kod örneğini barındırmakta. Eğer paralel programlamaya ilgi duyuyorsanız tabi <img class="wlEmoticon wlEmoticon-smile" style="border-style: none;" src="/pics/wlEmoticon-smile_19.png" alt="Gülümseme" /> Buraya kadar sabırla okuduğunuz için teşekkür ederken tekrardan görüşünceye dek hepinize mutlu günler dilerim. <a href="https://www.buraksenyurt.com/pics/2011%2f10%2fSortingTest.rar">SortingTest.rar (29,66 kb)</a></p>2011-10-17T09:47:00+00:00parallel programmingbarrier classtask parallel librarysort algoritmalarıbsenyurtDurumu daha iyi analiz edebilmek için dilerseniz basit bir senaryo üzerinden ilerlemeye çalışalım. Malumunuz günümüz üniversitelerinde özellikle algoritma derslerinde en çok sorulan, okutulan, ezberletilen konuların başında sıralama teknikleri gelmektedir. Bubble Sort, Quick Sort, Insertion Sort vb…https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=e67e0455-28df-43c8-89e6-36f0919053e50https://www.buraksenyurt.com/trackback.axd?id=e67e0455-28df-43c8-89e6-36f0919053e5https://www.buraksenyurt.com/post/Barrier-Class#commenthttps://www.buraksenyurt.com/syndication.axd?post=e67e0455-28df-43c8-89e6-36f0919053e5https://www.buraksenyurt.com/post/Composite-CancellationsComposite Cancellations2011-04-26T12:15:00+00:00bsenyurt<p><a href="https://www.buraksenyurt.com/pics/blg205_Giris.jpg"><img style="display: inline; margin-left: 0px; margin-right: 0px; border: 0px;" title="blg205_Giris" src="/pics/blg205_Giris_thumb.jpg" alt="blg205_Giris" width="302" height="202" align="right" border="0" /></a> Merhaba Arkadaşlar,</p>
<p>Hatırlayacağınız üzere bir önceki yazımızda (<a href="https://www.buraksenyurt.com/post/Task-Iptal-Islemlerinin-Izlenmesi(Monitoring-Cancellation)">Task İptal İşlemlerinin İzlenmesi(Monitoring Cancellation)</a> ) <strong>Task Cancellation</strong> işlemlerinin izlenmesi ile ilişkili teknikleri ve konuları irdelemeye başlamıştık. Bu yazımızda da iptal işlemleri ile ilgili farklı bir konuya değinmeye çalışıyor olacağız. Bu gün kü konumuz <strong>Composite Cancellation</strong> vakası.</p>
<p>Bildiğiniz üzere <strong>Task</strong> iptal taleplerinde, <strong>CancellationTokenSource</strong> örneğine ait <strong>Cancel</strong> metodunun çağırılması gerekmektedir. <strong>CancellationTokenSource</strong> örneği üzerinden yapılan iptal taleplerinin hangi <strong>Task</strong> işleyişini keseceğinin belirlenmesinde ise <strong>CancellationToken</strong> örneklerinden yararlanılmaktadır. <strong>CancellationToken</strong> örnekleri hatırlayacağını üzere <strong>CancellationTokenSource </strong>örnekleri tarafından üretilmekte ve <strong>Task</strong>’ ler ile ilişkilendirilmektedir. Bu sebepten <strong>Cancel</strong> metodunun hangi <strong>Task</strong> ile ilişkili olduğu bellidir. Bir önceki yazımızda geliştirdiğimiz son örnekte aynı <strong>Token</strong> örneğini kullanan birden fazla <strong>Task’</strong> in tek bir iptal çağrısı ile nasıl kesilebileceğini incelemiştik. Bir diğer durum da şudur;</p>
<blockquote>
<p>Birden fazla CancellationTokenSource birbirlerine bağlanarak bir zincir oluşturulabilir ve bunlardan herhangibiri üzerinden Cancel işleminin yapılması, zincirdeki tüm source’ lar için de aynı talebin gerçekleştirilmesi anlamına gelmektedir.</p>
</blockquote>
<p>Konuyu daha net kavrayabilmek için aşağıdaki kod parçasını göz önüne alabiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace CancellationScenarios2
{
class Program
{
static void Main(string[] args)
{
IEnumerable<int> numbers = Enumerable.Range(0, 100000000);
CancellationTokenSource tokenSource1 = new CancellationTokenSource();
CancellationTokenSource tokenSource2 = new CancellationTokenSource();
CancellationTokenSource tokenSource3 = new CancellationTokenSource();
CancellationTokenSource compositeSource = CancellationTokenSource.CreateLinkedTokenSource(tokenSource1.Token, tokenSource2.Token,tokenSource3.Token);
Task startedTask = Task.Factory.StartNew(() =>
{
for (int i = 0; i < numbers.Count(); i++)
{
compositeSource.Token.ThrowIfCancellationRequested();
i++;
i--;
i *= 2;
Console.Write(".");
}
}
, compositeSource.Token);
Task startedTask2 = Task.Factory.StartNew(() =>
{
for (int i = 0; i < numbers.Count(); i++)
{
compositeSource.Token.ThrowIfCancellationRequested();
i++;
i--;
i++;
i *= 2;
Console.Write("+");
}
}
, compositeSource.Token);
Task startedTask3 = Task.Factory.StartNew(() =>
{
for (int i = 0; i < numbers.Count(); i++)
{
compositeSource.Token.ThrowIfCancellationRequested();
i++;
i *= 2;
Console.Write("/");
}
}
, compositeSource.Token);
compositeSource.Token.Register(() =>
{
Console.WriteLine("İşlem iptali");
}
);
Console.WriteLine("İşlemler devam ediyor. İptal etmek için bir tuşa basınız");
Console.ReadLine();
tokenSource3.Cancel();
Console.ReadLine();
Console.WriteLine("Task 1 Status = {0}\nTask 2 Status = {1}\nTask 3 Status = {2}", startedTask.Status, startedTask2.Status,startedTask3.Status);
}
}
}</pre>
<p>Örnek uygulamamızda 3 adet <strong>CancellationTokenSource</strong> nesnesi örneklendiği görülmektedir. Sonrasında ise bu örnekler <strong>CancellationTokenSource</strong> sınıfının<strong> static CreateLinkedTokenSource</strong> metodu yardımıyla birbirlerine bağlanmıştır. 3 Task örneği oluşturulduğu sırada yapılan <strong>Token</strong> bildirimlerinde, <strong>CreateLinkedTokenSource</strong> metodu ile üretilen <strong>CancellationTokenSource</strong> nesne örneğine ait <strong>Token</strong> özelliğinden yararlanılmıştır.</p>
<p>Kodun ilerleyen kısımlarında <strong>Task</strong> fonksiyonellikleri icra ettikleri işleri tamamlanmadan önce kullanıcı bir tuşa basarsa <strong>tokenSource3</strong> isimli değişken üzerinden yapılan <strong>Cancel</strong> çağırısı devreye girmektedir. Buna göre zincir içerisinde yer alan <strong>CancellationTokenSource</strong> örneklerinin her biri için bir iptal talebi söz konusu olacaktır. Dolayısıyla uygulamanın çalışması sonrasında aşağıdaki ekran görüntüsüne benzer sonuçlar üretilecektir.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg205_CompositeTest1.gif"><img style="display: inline; border-width: 0px;" title="blg205_CompositeTest1" src="/pics/blg205_CompositeTest1_thumb.gif" alt="blg205_CompositeTest1" width="405" height="119" border="0" /></a> </p>
<p>Dikkat edileceği üzere 3 Task içinde <strong>Status</strong> değeri <strong>Canceled</strong> olmuştur. <strong>CancellationTokenSource</strong> örneklerinin birden fazla olduğu ve herhangibiri üzerinde yapılan iptal işleminin diğerleri içinde söz konusu olması gerektiği durumlarda bu teknikten yararlanılabilir. İlk örneğimizdeki durumu kafamızda daha kolay canlandırmak için aşağıdaki tasviri göz önüne alabiliriz.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg205_Schema.gif"><img style="display: inline; border-width: 0px;" title="blg205_Schema" src="/pics/blg205_Schema_thumb.gif" alt="blg205_Schema" width="367" height="319" border="0" /></a></p>
<p>Şimdi konuyu farklı bir bakış açısından değerlendirmek için kodu biraz değiştirip aşağıdaki hale getirelim.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace CancellationScenarios2
{
class Program
{
static void Main(string[] args)
{
IEnumerable<int> numbers = Enumerable.Range(0, 100000000);
CancellationTokenSource tokenSource1 = new CancellationTokenSource();
CancellationTokenSource tokenSource2 = new CancellationTokenSource();
CancellationTokenSource tokenSource3 = new CancellationTokenSource();
CancellationTokenSource compositeSource = CancellationTokenSource.CreateLinkedTokenSource(tokenSource1.Token, tokenSource2.Token);
Task startedTask = Task.Factory.StartNew(() =>
{
for (int i = 0; i < numbers.Count(); i++)
{
compositeSource.Token.ThrowIfCancellationRequested();
i++;
i--;
i *= 2;
Console.Write(".");
}
}
, compositeSource.Token);
Task startedTask2 = Task.Factory.StartNew(() =>
{
for (int i = 0; i < numbers.Count(); i++)
{
compositeSource.Token.ThrowIfCancellationRequested();
i++;
i--;
i++;
i *= 2;
Console.Write("+");
}
}
, compositeSource.Token);
Task startedTask3 = Task.Factory.StartNew(() =>
{
for (int i = 0; i < numbers.Count(); i++)
{
tokenSource3.Token.ThrowIfCancellationRequested();
i++;
i *= 2;
Console.Write("/");
}
}
, tokenSource3.Token);
compositeSource.Token.Register(() =>
{
Console.WriteLine("İşlem iptali");
}
);
Console.WriteLine("İşlemler devam ediyor. İptal etmek için bir tuşa basınız");
Console.ReadLine();
tokenSource1.Cancel();
Console.ReadLine();
Console.WriteLine("Task 1 Status = {0}\nTask 2 Status = {1}\nTask 3 Status = {2}", startedTask.Status, startedTask2.Status,startedTask3.Status);
}
}
}</pre>
<p>Bu örnekte sadece tokenSource1 ve tokenSource2 örnekleri birbirlerine bağlanmış ancak tokenSource3 bu zincirin dışında tutulmuştur. Buna göre tokenSource1.Cancel çağrısının gerçekleştirilmesi sonucunda sadece zincir içerisinde dahil edilmiş Task’ lerin iptal işlemi gerçekleşecektir. Çalışma zamanı çıktısında bu durum net bir şekilde görülebilir.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg205_CompositeTest2.gif"><img style="display: inline; border-width: 0px;" title="blg205_CompositeTest2" src="/pics/blg205_CompositeTest2_thumb.gif" alt="blg205_CompositeTest2" width="409" height="126" border="0" /></a></p>
<p>Görüldüğü gibi <strong>Task 1</strong> ve <strong>2 </strong>için <strong>Status</strong> değerleri <strong>Canceled</strong> olarak set edilmişken, <strong>Task 3</strong> çalışmaya devam ettiğinden <strong>Running</strong> değerini almıştır.</p>
<p>Böylece geldik bir yazımızın daha sonuna. Paralel programlama ile ilişkili konuları incelemeye devam ediyor olacağız. Bundan sonraki yazımızda <strong>Task'</strong> lerin belirli süreler boyunca bekletilmesi amacıyla kullanabileceğimiz teknikleri araştırıyor ve örnekliyor olacağız. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2010%2f6%2fCancellationScenarios2.rar">CancellationScenarios2.rar (21,79 kb)</a><strong> [Örnek Visual Studio 2010 Ultimate sürümünde geliştirilmiş ve test edilmiştir]</strong></p>2011-04-26T12:15:00+00:00c# 4.0parallel programmingcancellationtokensourcecancellationtokenvisual studio 2010 ultimatetask parallel librarytask cancellationmonitoring cancellationcomposite cancellationtokensourcebsenyurtBildiğiniz üzere Task iptal taleplerinde, CancellationTokenSource örneğine ait Cancel metodunun çağırılması gerekmektedir. CancellationTokenSource örneği üzerinden yapılan iptal taleplerinin hangi Task işleyişini keseceğinin belirlenmesinde ise CancellationToken örneklerinden yararlanılmaktadır.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=8c489b3f-415c-4a2d-830c-9096a01959280https://www.buraksenyurt.com/trackback.axd?id=8c489b3f-415c-4a2d-830c-9096a0195928https://www.buraksenyurt.com/post/Composite-Cancellations#commenthttps://www.buraksenyurt.com/syndication.axd?post=8c489b3f-415c-4a2d-830c-9096a0195928https://www.buraksenyurt.com/post/Task-Iptal-Islemlerinin-Izlenmesi(Monitoring-Cancellation)Task İptal İşlemlerinin İzlenmesi(Monitoring Cancellation)2011-04-23T12:10:00+00:00bsenyurt<p><a href="https://www.buraksenyurt.com/pics/blg204_Giris.jpg"><img style="display: inline; margin-left: 0px; margin-right: 0px; border-width: 0px;" title="blg204_Giris" src="/pics/blg204_Giris_thumb.jpg" alt="blg204_Giris" width="209" height="302" align="right" border="0" /></a>Merhaba Arkadaşlar,</p>
<p>Bu yazımızda daha önceden<strong> .Net Framework Beta 1</strong> ve <strong>Beta 2 </strong>sürümlerinde incelediğimiz <strong>Task</strong> iptal işlemlerini son sürümde ele alıp toparlamaya çalışıyor olacağız. Task iptal işlemleri oldukça önemli ve üzerinde titizlikle durulması gereken bir konudur. Nitekim bazı hallerde çalıştırılmakta olan <strong>Task </strong>işlevlerinin iptal edilmesi gerekebilir. Bu iptal işlemi, sistem tarafından her hangibir koşulun gerçeklenmesi sonucu talep edilebileceği gibi, kullanıcı tarafından da uygulatılmak istenebilir.</p>
<p>Task örnekleri işaret ettikleri ve planladıkları fonksiyonellikleri çalıştırdıklarında ve herhangibir sebeple iptal işlemi gerçekleştirilmek istendiğinde <strong>CancellationTokenSource </strong>ve <strong>CancellationToken </strong>tiplerinden yararlanılması gerekmektedir.</p>
<p>Bu amaçla İptal işlemlerinde <strong>CancellationTokenSource</strong> nesne örnekleri üzerinden <strong>Cancel</strong> metodunun çağırılması yeterlidir. Ancak burada da dikkat edilmesi gereken bir husus vardır. Çalışma zamanı <strong>Cancel</strong> çağrısı sonucu ilgili <strong>Task</strong> örneklerinin işleyişlerini otomatik olarak <span style="text-decoration: underline;">sonlandırmaz</span>. Bir başka deyişle <strong>Task</strong> gövdeleri veya paralel yürütülen metod blokları içerisinde <strong>Cancel</strong> işleminin talep edilip edilmediğinin takibi gerekmektedir(<strong>Monitoring Cancellation</strong>). Peki bu takip işlemleri nasıl gerçekleştirilebilir. Yazımızın ilerleyen kısımlarında bu takip işlemlerinde hangi konuların ele alındığını irdelemeye çalışıyor olacağız.</p>
<h2><strong>Polling Tekniğini ile İptal Taleplerinin İzlenmesi</strong></h2>
<p>Bu teknik adından da anlaşılacağı üzere bir iptal talebinin yapılıp yapılmadığını sürekli olarak denetlemeyi gerektirir. Bu durumu analiz etmek için <strong>Visual Studio 2010 Ultimate</strong> ortamında geliştirilmiş aşağıdaki kod parçasını göz önüne alabiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace CancellationScenarios
{
class Program
{
static void Main(string[] args)
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
IEnumerable<int> numbers =Enumerable.Range(0, 100000000);
Task startedTask=Task.Factory.StartNew(() =>
{
for(int i=0;i<numbers.Count();i++)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("İşlemler {0}. elemandan sonra iptal edildi",i.ToString());
throw new OperationCanceledException(token);
}
else
{
i++;
i--;
i *= 2;
Console.Write(".");
}
}
}
, token);
Console.WriteLine("İşlemler devam ediyor. İptal etmek için bir tuşa basınız");
Console.ReadLine();
tokenSource.Cancel();
Console.ReadLine();
Console.WriteLine("Task Status = {0}",startedTask.Status);
}
}
}</pre>
<p>İlk olarak <strong>CancellationTokenSource</strong> nesnesi örneklenmiş ve sonrasında bu örneğin <strong>Token</strong> özelliğinden yararlanılarak bir <strong>CancellationToken</strong> üretilmiştir. <strong>CancellationToken</strong> nesne örneği <strong>Task</strong> tipinin örneklenmesi sırasında da parametre olarak aktarılmaktadır.</p>
<p>Dikkat edileceği üzere <strong>for</strong> döngüsü içerisinde parametre olarak gelen <strong>token</strong> değişkeni üzerinden <strong>IsCancellationRequested</strong> özelliği kontrol edilmektedir. Kontrol işlemi döngünün her bir iterasyonunda söz konusudur. <strong>IsCancellationRequested</strong> özelliğinin <strong>true</strong> değerini alması için <strong>CancellationToken</strong> ile ilişkilendirilmiş olan <strong>CancellationTokenSource</strong> nesne örneği üzerinden <strong>Cancel</strong> metodunun çağırılması gerekmektedir. Örneği bu haliyle çalıştırdığımızda ve işlemler devam ederken herhangibir noktada herhangibir tuşa bastığımızda ise aşağıdaki ekran görüntüsüne benzer sonuçlar ile karşılaşırız.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg204_PollingTest.gif"><img style="display: inline; border-width: 0px;" title="blg204_PollingTest" src="/pics/blg204_PollingTest_thumb.gif" alt="blg204_PollingTest" width="415" height="104" border="0" /></a></p>
<p>Önemli olan noktalardan birisi de başlatılan <strong>Task’</strong> in <strong>Status</strong> değeridir. Dikkat edileceği üzere bu değer <strong>Canceled</strong> olarak set edilmiştir. Ancak bunun böyle olmasının sebebi iptal işleminin kontrol edildiği noktadan <strong>OperationCanceledException</strong> tipinden istisna nesnesinin fırlatılmasıdır. Bu istisnanın <span style="text-decoration: underline;">üretilmemesi</span> sonucu ise aşağıdaki gibi olacaktır ki bu da <strong>Task</strong> durumunu değerlendiren kod parçaları için tam anlamıyla bir handikaptır.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg204_PollingTest2.gif"><img style="display: inline; border-width: 0px;" title="blg204_PollingTest2" src="/pics/blg204_PollingTest2_thumb.gif" alt="blg204_PollingTest2" width="407" height="109" border="0" /></a></p>
<p>Görüldüğü gibi <strong>Exception</strong> fırlatılmadığı için <strong>Task</strong> durumu <strong>Running</strong> olarak kalmıştır. Oysaki <strong>Task</strong> iptal edilmiştir.</p>
<p>Polling modelinde uygulanan genel kod deseni yukarıdaki gibi olmasına rağmen ikinci bir teknik daha söz konusudur. Bu teknikte kontrol etme ve Exception fırlatma işlemleri zaten var olan bir metoda yüklenmiştir. Buna göre for döngüsü içeriğini aşağıdaki gibi değiştirdiğimizi düşünelim.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">for(int i=0;i<numbers.Count();i++)
{
token.ThrowIfCancellationRequested();
i++;
i--;
i *= 2;
Console.Write(".");
}</pre>
<p>Bu durumda da çalışma zamanında aşağıdaki sonuçları alırız.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg204_PollingTest3.gif"><img style="display: inline; border-width: 0px;" title="blg204_PollingTest3" src="/pics/blg204_PollingTest3_thumb.gif" alt="blg204_PollingTest3" width="408" height="92" border="0" /></a></p>
<p><strong>Polling</strong> tekniği özellikle <strong>Task</strong> gövdelerinin döngüsel işlemler yaptığı senaryolar için uygundur. Nitekim döngünün her iterasyonu sırasında, bir iptal isteği olup olmadığı kontrol edilmekte ve eğer varsa, döngü dışına çıkılması, ortama uygun <strong>İstisna(Exception)</strong> nesnesi fırlatılması, gerekiyorsa ilgili<strong> kaynakların(Resources)</strong> serbest bırakılması işlemleri gerçekleştirilmektedir.</p>
<h2><strong>Delegate Kullanımı</strong></h2>
<p><strong>Polling</strong> modeli kullanışlı olmasına rağmen özellikle <strong>CancellationToken</strong> üzerinden <strong>ThrowIfCancellationRequested</strong> metodu kullanıldığında, sanki eksik olan bir şey var hissi uyandırmaktadır. <strong>6ncı</strong> hissimiz bize bu noktada şunu söylemektedir; <strong>User Interface’</strong> e sahip uygulamalarda kullanıcıların işlemin iptal edildiğine dair bilgilendirilmesi nasıl sağlanacaktır? Bu his biraz daha genişletilebilir ama sonuç itibariyle iptal işlemi sonrası ana uygulamanın bilgilendirilmesi önemlidir. Özellikle işlemlerin loglandığı, IO süreçlerinin söz konusu olduğu durumlarda. Bu tip bir vaka da delegasyon kullanımı ile iptal işleminin ele alınması tercih edilebilir. Aşağıdaki örnek kod parçasında bu durum irdelenmektedir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace CancellationScenarios
{
class Program
{
static void Main(string[] args)
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
IEnumerable<int> numbers = Enumerable.Range(0, 100000000);
Task startedTask = Task.Factory.StartNew(() =>
{
for (int i = 0; i < numbers.Count(); i++)
{
token.ThrowIfCancellationRequested();
i++;
i--;
i *= 2;
Console.Write(".");
}
}
, token);
token.Register(() =>
{
Console.WriteLine("İptal işlem gerçekleştirildi. Tam bu sırada Task Status = {0}",startedTask.Status);
}
);
Console.WriteLine("İşlemler devam ediyor. İptal etmek için bir tuşa basınız");
Console.ReadLine();
tokenSource.Cancel();
Console.ReadLine();
Console.WriteLine("Task Status = {0}", startedTask.Status);
}
}
}</pre>
<p>Dikkat edileceği üzere token nesnesi üzerinden <strong>Register</strong> metodu kullanılarak bir delegasyon yapılmaktadır. Buna göre <strong>tokenSource.Cancel </strong>çağrısı gerçekleştirildiğinde, <strong>Register</strong> metodu ile işaret edilen blok otomatik olarak devreye girecektir. Örneğin çalışma zamanı çıktısı aşağıdaki ekran görüntüsündekine benzer olacaktır.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg204_DelegationTest1.gif"><img style="display: inline; border-width: 0px;" title="blg204_DelegationTest1" src="/pics/blg204_DelegationTest1_thumb.gif" alt="blg204_DelegationTest1" width="484" height="102" border="0" /></a></p>
<p>Dikkat edilmesi gereken notkalardan birisi de, delegasyon metodu içerisine girildiğinde iptal edilen Task örneğinin durumunun halen Running olarak görünmesidir. Ancak işlem tamamlandıktan sonra Status değeri Canceled olmaktadır.</p>
<p><a href="https://www.buraksenyurt.com/pics/Question.gif"><strong><img style="display: inline; margin-left: 0px; margin-right: 0px; border-width: 0px;" title="Question" src="/pics/Question_thumb.gif" alt="Question" width="48" height="48" align="left" border="0" /></strong></a><em><strong>Bloğun sorusu; Register</strong> ile işaret edilen delegasyon metodu içerisinden, <strong>for</strong> döngüsünün hangi iterasyonunda iptal işleminin gerçekleştirildiği çalışma ortamına nasıl bildirilebilir?</em></p>
<h2><strong>Wait Handle Kullanımı</strong></h2>
<p><strong>Task</strong> İptal işlemlerinde delegasyon kullanımına alternatif bir yol olarak <strong>Wait Handle</strong> tekniğinden de yararlanılabilir. Bu kullanım şeklini daha net bir kavrayabilmek adına aşağıdaki kod parçasını göz önüne alalım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace CancellationScenarios
{
class Program
{
static void Main(string[] args)
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
IEnumerable<int> numbers = Enumerable.Range(0, 100000000);
Task startedTask = Task.Factory.StartNew(() =>
{
for (int i = 0; i < numbers.Count(); i++)
{
token.ThrowIfCancellationRequested();
i++;
i--;
i *= 2;
Console.Write(".");
}
}
, token);
Task handleTask = Task.Factory.StartNew(() =>
{
token.WaitHandle.WaitOne();
Console.WriteLine("İşlem iptali.");
}
);
Console.WriteLine("İşlemler devam ediyor. İptal etmek için bir tuşa basınız");
Console.ReadLine();
tokenSource.Cancel();
Console.ReadLine();
Console.WriteLine("Task Status = {0}", startedTask.Status);
}
}
}</pre>
<p><a href="https://www.buraksenyurt.com/pics/blg204_WaitHandleTest.gif"><img style="display: inline; border-width: 0px;" title="blg204_WaitHandleTest" src="/pics/blg204_WaitHandleTest_thumb.gif" alt="blg204_WaitHandleTest" width="416" height="109" border="0" /></a></p>
<p>Bu kullanım şeklinde ikinci bir <strong>Task </strong>bloğunun rol alması gözden kaçmamalıdır ;) <strong>handleTask</strong> isimli <strong>Task</strong>’ e ait kod bloğu içerisinde, <strong>CancellationToken</strong> nesnesi üzerinden <strong>WaitHandle.WaitOne</strong> çağrısının gerçekleştirildiği görülmektedir. Aslında çalışma zamanında her hangibir anda <strong>Task</strong> durumlarına bakıldığında aşağıdaki ekran görüntüsündekine benzer sonuçlarla karşılaşıldığı görülecektir.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg204_Breakpoint2.gif"><img style="display: inline; border-width: 0px;" title="blg204_Breakpoint2" src="/pics/blg204_Breakpoint2_thumb.gif" alt="blg204_Breakpoint2" width="644" height="220" border="0" /></a></p>
<p>Dikkat edileceği üzere <strong>handleTask</strong> örneğinin durumu <strong>Waiting</strong> olarak görülmektedir. Bu son derece doğaldır nitekim <strong>Task</strong> örneğinin başlatılması sonrasında devreye giren fonksiyon bloğunun daha ilk satırında <strong>WaitOne</strong> ile <strong>Thread</strong> ‘ in bekletilmesi söz konusudur.</p>
<p><strong>CancellationTokenSource </strong>nesne örneği üzerinden <strong>Cancel</strong> metodunun çağırılması sonrasında ise <strong>WaitOne</strong> metod çağrısını takip eden kod satırına girildiği ve <strong>startedTask</strong> örneğinin icra etmekte olduğu işlemlerinde iptal edildiği görülebilir.</p>
<p>Buraya kadar geliştirdiğimiz örnek kod parçaları ve iptal deseneleri göz önüne alındığında her zaman için <strong>CancellationTokenSource</strong> üzerinden üretilen bir <strong>CancellationToken</strong> nesneleri olduğu görülmektedir. Ayrıca iptal işlemi de her zaman için <strong>CancellationTokenSource</strong> örneği üzerinden yapılmaktadır. <strong>CancellationToken</strong> örnekleri ise <strong>Task</strong> üretimi sırasında ve içeride kullanılmaktadır. Peki bu bizim ne işimize yarayabilir?</p>
<p><strong>Birden Fazla Task İşleminin Tek Noktadan İptal Edilmesi</strong></p>
<p>İşte az önceki sorunun cevabı. Aynı <strong>CancellationToken</strong> nesne örneğinin birden fazla <strong>Task</strong> ile ilişkilendirilmesi, bu token örnekleri ile ilişkili olan <strong>CancellationTokenSource</strong> örneği üzerinden yapılan iptal talebinin, tüm ilişkili <strong>Task’</strong> lere iletilmesi anlamına gelmektedir. Gelin bu cümleyi aşağıdaki kod parçası ile anlamaya çalışalım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace CancellationScenarios
{
class Program
{
static void Main(string[] args)
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
IEnumerable<int> numbers = Enumerable.Range(0, 100000000);
IEnumerable<int> numbers2 = Enumerable.Range(0, 100000000);
Task startedTask = Task.Factory.StartNew(() =>
{
for (int i = 0; i < numbers.Count(); i++)
{
token.ThrowIfCancellationRequested();
i++;
i--;
i *= 2;
Console.Write(".");
}
}
, token);
Task startedTask2 = Task.Factory.StartNew(() =>
{
for (int i = 0; i < numbers2.Count(); i++)
{
token.ThrowIfCancellationRequested();
i++;
i--;
i *= 2;
Console.Write("/");
}
}
, token);
token.Register(() =>
{
Console.WriteLine("İşlem iptali");
}
);
Console.WriteLine("İşlemler devam ediyor. İptal etmek için bir tuşa basınız");
Console.ReadLine();
tokenSource.Cancel();
Console.ReadLine();
Console.WriteLine("Task 1 Status = {0}\nTask 2 Status = {1}", startedTask.Status,startedTask2.Status);
}
}
}</pre>
<p>Kod parçasında iki farklı <strong>Task</strong> içeriği söz konusudur. Ancak her iki <strong>Task</strong> örneklenirken aynı <strong>CancellationToken</strong> ile ilişkilendirilmiştir. Buna göre <strong>CancellationTokenSource</strong> üzerinden yapılacak olan iptal talebi, her iki task işlemi içinde istenmiş olmaktadır. Örnek çalışma zamanı çıktısı aşağıdaki şekilde görüldüğü gibidir.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg204_MultipleCancel.gif"><img style="display: inline; border-width: 0px;" title="blg204_MultipleCancel" src="/pics/blg204_MultipleCancel_thumb.gif" alt="blg204_MultipleCancel" width="414" height="112" border="0" /></a> </p>
<p>Görüldüğü gibi her iki Task örneğinin durumu Canceled olarak set edilmiştir. Task iptal işlemleri ile ilişkili olarak ele almamız gereken farklı konular da mevcuttur. Bunları bir sonraki yazımızda ele almaya çalışıyor olacağız. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2010%2f6%2fCancellationScenarios.rar">CancellationScenarios.rar (23,87 kb)</a><strong> [Örnekler Visual Studio 2010 Ultimate sürümü üzerinde geliştirişmiş ve test edilmiştir]</strong></p>2011-04-23T12:10:00+00:00parallel programmingtask parallel librarycancellationtokensourcetask cancellationcancellationtokenc# 4.0visual studio 2010 ultimatemonitoring cancellationbsenyurtBu yazımızda daha önceden .Net Framework Beta 1 ve Beta 2 sürümlerinde incelediğimiz Task iptal işlemlerini son sürümde ele alıp toparlamaya çalışıyor olacağız.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=ae83aa80-a69b-4644-8a14-fba3d9e7cbfd1https://www.buraksenyurt.com/trackback.axd?id=ae83aa80-a69b-4644-8a14-fba3d9e7cbfdhttps://www.buraksenyurt.com/post/Task-Iptal-Islemlerinin-Izlenmesi(Monitoring-Cancellation)#commenthttps://www.buraksenyurt.com/syndication.axd?post=ae83aa80-a69b-4644-8a14-fba3d9e7cbfd