https://www.buraksenyurt.com/Burak Selim Şenyurt - TPL2012-10-30T04:51:20+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/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/TPL-Senkronizasyonu-SaglamakTPL Senkronizasyonu Sağlamak - 12011-01-27T13:40:00+00:00bsenyurt<p><img style="display: inline; margin-left: 0px; margin-right: 0px; border: 0px;" title="blg212_Giris" src="/pics/blg212_Giris.jpg" alt="blg212_Giris" width="204" height="304" align="right" border="0" /> Merhaba Arkadaşlar,</p>
<p>Pek çoğumuzun anahtarlığında sayısız anahtar bulunmaktadır. Özellike gerilim filmlerinde bu anahtarlardan doğru olanı bulmak ve anahtar deliğine sokmak, hep zaman alan başarısız kaçış girişimleri olarak sahnelenir. Genellikle bu başarız girişimlerin sonunda ne olduğu malumdur.</p>
<p>Ancak ister gerilim filmi olsun ister olmasın sonuçta anahtar deliğine herhangibir zamanda takılabilecek sadece tek bir anahtar söz konusudur. Üstelik bu anahtar, aynı yere başka bir anahtarın takılmasına da izin vermez. Aslında izin verip vermemesi, anahtarı tutan veya kullanan kişinin elindedir.</p>
<p>Aslında şu anda varmak istediğim nokta <strong>lock</strong> kelimesidir. <strong>lock</strong>, çok kanallı uygulamalarda <strong>Thread</strong> senkronizasyon işlemlerinde ele alınan tekniklerden yanlızca birisidir. Çok doğal olarak farklı amaçlarla ele alınan tipler de söz konusudur.</p>
<p>Tüm bunlara ek olarak uzun zamandır bilinen <strong>Task Parallel Library </strong>ve doğal olarak yeni paralel programlama yapısı mevcuttur. Dolayısıyla aynı senkronizasyon vakaları <strong>TPL </strong>içerisinde de geçerlidir ve bu amaçla eklenmiş yeni özellikler bulunmaktadır. İşte bu yazımız ile birlikte <strong>TPL</strong> içerisinde senkronizasyon konusunu incelemeye çalışıyor olacağız.</p>
<p><a href="https://www.buraksenyurt.com/post/TPL-ve-Shared-Data-Isolation">TPL ve Shared Data Isolation</a> başlıklı yazımızda, <strong>n</strong> <strong>sayıda</strong> <strong>Task</strong> örneğinin ortaklaşa kullandıkları bir veri alanı üzerindeki işlemlerinin, ne gibi sonuçlara yol açabileceğini incelemiş ve bunun önün geçmek için basit bir kaç yolu ele almıştık. Hatırlayacağınız üzere değerlendirdiğimiz senaryoda, <strong>Plane</strong> tipinden olan nesne örneğine ait <strong>Altitude</strong> özelliğinin değerinin, <strong>Task</strong> blokları içerisinde değiştirildiği nokta, sorunun oluşmasına neden olan kritik bölgeydi.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">for (int i = 0; i < 5; i++)
{
tasks[i] = new Task(() =>
{
for (int j = 0; j < 1000; j++)
{
f16.Altitude += j - 5;
}
}
);
tasks[i].Start();
}</pre>
<p>Aslında ezelden beri <strong>Multi-Thread</strong> programlamada, <strong>Thread’</strong> lerin senkronizasyonu konusu öyle ya da böyle bir şekilde kulağımıza gelmiştir. Çok doğal olarak benzer ihtiyaçlar <strong>.Net Framework 4.0 </strong>Paralel Programlama alt yapısı içinde geçerlidir. Ancak <strong>.Net Framework 4.0 </strong>tarafında <strong>Lightweight Primitives </strong>adında yeni ve daha basit senkronizasyon tipleri de söz konusudur. Bu tipler klasik senkronizasyon tiplerine göre daha kola uygulanabilir. Tabi klasik senkronizasyon <strong>Primitive</strong>’ lerinin uygulanması her ne kadar zor olsa da, birden fazla <strong>Application Domain</strong> üzerinde kontrole izin vermektedirler. Oysa ki <strong>LightWeight Primitive</strong>’ ler sadece tek bir<strong> Application Domain</strong> için uygulanabilir. Olayı daha fazla karmaşıklaştırmadan önce senkronizasyon ile neyi ifade ettiğimizi ortaya koymamızda yarar vardır.</p>
<p><strong><a href="https://www.buraksenyurt.com/pics/Exclamation_3.gif"><img style="display: inline; margin-left: 0px; margin-right: 0px; border: 0px;" title="Exclamation" src="/pics/Exclamation_thumb_3.gif" alt="Exclamation" width="48" height="48" align="left" border="0" /></a> “Herhangibir t anında kritik olan bölgeyde sadece tek bir Task örneğinin işlem yapmasını sağlamak”</strong></p>
<p>Senkronizasyonu sağlamak için <strong>.Net Framework </strong>üzerinde önceden tanımlı çeşitli <strong>Primitive</strong>’ lerden yararlanılmaktadır. Aslında özel veri tipleri olarak düşünebileceğimiz <strong>Primitive</strong>’ ler, kritik bölgelere doğru yapılan <strong>Task</strong> erişimlerini kontrol altına alan varlıklar olarak düşünülebilirler.</p>
<p>Genel işleyiş şekline bakıldığında, kritik bölgede yer alan veriye erişmek isteyen bir <strong>Task</strong> örneği, bu isteğini ilk önce <strong>Primitive</strong>’ e iletmektedir. Eğer söz konusu kritik bölge müsait ise, <strong>Task</strong> işlemlerini icra etmeye başlayabilir. Ancak müsait değilse, <strong>Task</strong>, <strong>Primitive</strong> nesnesinin belirlediği ilkelere göre beklemede kalacaktır. Bu anda, söz konusu bölgede çalışmakta olan <strong>Task</strong> örneği de, işini bitirdiğinde <strong>Primitive</strong>’ e bildirimde bulunacaktır. Bu bildirimin ardından <strong>Primitive</strong> nesne, bekleyen <strong>Task</strong> örneğinin söz konusu bölgede işlem yapmasına izin verecektir. Tabi işlem yapmaya başlayan <strong>Task</strong> örneği, söz konusu bölgeyi en azından kilitlediğini, <strong>Primitive</strong>’ e bildirecektir. Elbette gerçek hayat senaryolarında durum bu anlatım şeklinde olduğu gibi basit değildir. <strong>N</strong> sayıda <strong>Task</strong> söz konusu olduğunda birbirlerine göre önceliklerini belirlemek gibi ihtiyaçlarımız da olabilir.</p>
<p>Bu yazımızla birlikte söz konusu senkronizasyon tiplerini basit bir şekilde incelemeye başlıyor olacağız. Haydi başlayalım.</p>
<h2><strong>En Basit Askerimiz : lock</strong></h2>
<p><strong>lock</strong> anahtar kelimesi aslında <strong>System.Threading.Monitor</strong> sınıfının daha basit bir şekilde uygulanmasını sağlayan bir <strong>Primitive</strong> olarak düşünülebilir. Normal şartlar altında kritik bölgeyle olan etkileşimlerde <strong>Monitor</strong> tipinin <strong>Enter, TryEnter, Exit</strong> gibi metodlarının kullanılması gerekmektedir. <strong>Monitor</strong> tipi <strong>HeavyWeight</strong> <strong>Primitive</strong> olarak bilinmektedir ve daha alt seviyedeki vakaların karşılanmasında kullanılmaktadır. Aşağıdaki kod parçasında daha önceki yazımızda ele aldığımız sorunlu senaryonun <strong>lock</strong> ile çözümü gösterilmektedir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Threading.Tasks;
namespace SynchronizationPrimitives
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
TestMethod();
}
Console.WriteLine("İşlemler tamamlandı.\nProgramı kapatmak için bir tuşa basınız");
Console.ReadLine();
}
private static void TestMethod()
{
Plane f16 = new Plane();
Task[] tasks = new Task[5];
object obj = new object();
for (int i = 0; i < 5; i++)
{
tasks[i] = new Task(() =>
{
for (int j = 0; j < 1000; j++)
{
lock (obj)
{
f16.Altitude += j - 5;
}
}
}
);
tasks[i].Start();
}
Task.WaitAll(tasks);
Console.WriteLine("[ {0} ]", f16.Altitude);
}
}
class Plane
{
public int Altitude { get; set; }
}
}</pre>
<p>İlk olarak tüm <strong>Task</strong> örneklerinin, kritik bölgede işlem yapılırken <strong>lock</strong> bloğuna girdiklerini ifade edebiliriz. Bu <strong>lock</strong> bloğu içerisindeki çalışma süresi boyunca, yürütücü <strong>Task</strong> dışında başka bir <strong>Task </strong>örneğinin <strong>Altitude</strong> değerini değiştirmesine izin verilmemektedir. <strong>lock</strong> keyword’ ü kullanılırken <strong>object</strong> tipinden bir nesneyi parametre olarak alır. Bu nesne de aslında tüm <strong>Task</strong> örnekleri için ortaktır. Uygulamayı kaç kere çalıştırırsanız çalıştırın beklediğimiz aynı sonuçları elde ettiğimizi görebiliriz. Aynen aşağıdaki ekran görüntüsünde olduğu gibi.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg212_Runtime1.gif"><img style="display: inline; border: 0px;" title="blg212_Runtime1" src="/pics/blg212_Runtime1_thumb.gif" alt="blg212_Runtime1" width="336" height="181" border="0" /></a></p>
<p>Şimdi senaryomuzu biraz daha ilginçleştirelim. Bu sefer iki ayrı <strong>Task</strong> kümesinin, <strong>Altitude</strong> özelliği üzerinden farklı hesaplamalar yaptığını varsayacağız. Bu amaçla vaka kodumuzu aşağıdaki gibi geliştirdiğimizi düşünelim.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Threading.Tasks;
namespace SynchronizationPrimitives
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
TestMethod();
}
Console.WriteLine("İşlemler tamamlandı.\nProgramı kapatmak için bir tuşa basınız");
Console.ReadLine();
}
private static void TestMethod()
{
Plane f16 = new Plane();
Task[] taskSet1 = new Task[5];
Task[] taskSet2 = new Task[5];
for (int i = 0; i < 5; i++)
{
taskSet1[i] = new Task(() =>
{
for (int j = 0; j < 1000; j++)
{
f16.Altitude += j - 5;
}
}
);
taskSet1[i].Start();
}
for (int i = 0; i < 5; i++)
{
taskSet2[i] = new Task(() =>
{
for (int j = 0; j < 1000; j++)
{
f16.Altitude += j+7;
}
}
);
taskSet2[i].Start();
}
Task.WaitAll(taskSet1);
Task.WaitAll(taskSet2);
Console.WriteLine("[ {0} ]", f16.Altitude);
}
}
class Plane
{
public int Altitude { get; set; }
}
}</pre>
<p>Bu sefer <strong>taskSet1</strong> ve <strong>taskSet2</strong> isimli iki farklı <strong>Task</strong> dizisinin <strong>Altitude</strong> özelliği üzerinde gerçekleştirdiği farklı işlemler söz konusudur. Çok doğal olarak ortaklaşa kullanılan değişken söz konusu olduğundan çalışma zamanında aynı sonuçların elde edilmesi nadir bir durumdur. Uygulamanın aşağıdaki örnek çıktısında bu durum açık bir şekilde görülebilir.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg212_Case.gif"><img style="display: inline; border: 0px;" title="blg212_Case" src="/pics/blg212_Case_thumb.gif" alt="blg212_Case" width="329" height="185" border="0" /></a></p>
<p>Bildiğiniz üzere her denemenin aynı sonucu üretiyor olmasını beklemekteyiz. Peki <strong>lock</strong> mekanizmasını bu tip senaryoda nasıl kullanabiliriz? Aşağıdaki kod parçasında bu çözümleme işlemi değerlendirilmektedir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Threading.Tasks;
namespace SynchronizationPrimitives
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
TestMethod();
}
Console.WriteLine("İşlemler tamamlandı.\nProgramı kapatmak için bir tuşa basınız");
Console.ReadLine();
}
private static void TestMethod()
{
Plane f16 = new Plane();
Task[] taskSet1 = new Task[5];
Task[] taskSet2 = new Task[5];
object obj = new object();
for (int i = 0; i < 5; i++)
{
taskSet1[i] = new Task(() =>
{
for (int j = 0; j < 1000; j++)
{
lock (obj)
{
f16.Altitude += j - 5;
}
}
}
);
taskSet1[i].Start();
}
for (int i = 0; i < 5; i++)
{
taskSet2[i] = new Task(() =>
{
for (int j = 0; j < 1000; j++)
{
lock (obj)
{
f16.Altitude += j + 7;
}
}
}
);
taskSet2[i].Start();
}
Task.WaitAll(taskSet1);
Task.WaitAll(taskSet2);
Console.WriteLine("[ {0} ]", f16.Altitude);
}
}
class Plane
{
public int Altitude { get; set; }
}
}</pre>
<p>Dikkat edileceği üzere ilk örneğimizdeki kodlama stilinden farklı bir uygulanış biçimi yoktur. Dikkat edilmesi gereken nokta ise, her iki <strong>lock </strong>bloğu içinde aynı <strong>object</strong> örneğinin kullanılmış olmasıdır.</p>
<h2><strong>lock Aslında Montior Tipini Kullanır</strong></h2>
<p>Daha önceden de bahsettiğimiz üzere <strong>lock</strong> bloğu aslında <strong>Monitor</strong> tipinin ilgili metodlarını kullanmaktadır. Bu durum kodun arka planda üretilen <strong>IL</strong> çıktısında rahat bir şekilde görülebilir.</p>
<pre class="brush:plain;auto-links:false;toolbar:false" contenteditable="false">.method public hidebysig instance void '<TestMethod>b__3'() cil managed
{
// Code size 85 (0x55)
.maxstack 4
.locals init ([0] int32 j,
[1] bool '<>s__LockTaken1',
[2] object CS$2$0000,
[3] bool CS$4$0001)
IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0
IL_0003: br.s IL_0048
IL_0005: nop
IL_0006: ldc.i4.0
IL_0007: stloc.1
.try
{
IL_0008: ldarg.0
IL_0009: ldfld object SynchronizationPrimitives.Program/'<>c__DisplayClass6'::obj
IL_000e: dup
IL_000f: stloc.2
IL_0010: ldloca.s '<>s__LockTaken1'
IL_0012: call void [mscorlib]System.Threading.Monitor::Enter(object, bool&)
IL_0017: nop
IL_0018: nop
IL_0019: ldarg.0
IL_001a: ldfld class SynchronizationPrimitives.Plane SynchronizationPrimitives.Program/'<>c__DisplayClass6'::f16
IL_001f: dup
IL_0020: callvirt instance int32 SynchronizationPrimitives.Plane::get_Altitude()
IL_0025: ldloc.0
IL_0026: ldc.i4.7
IL_0027: add
IL_0028: add
IL_0029: callvirt instance void SynchronizationPrimitives.Plane::set_Altitude(int32)
IL_002e: nop
IL_002f: nop
IL_0030: leave.s IL_0042
} // end .try
finally
{
IL_0032: ldloc.1
IL_0033: ldc.i4.0
IL_0034: ceq
IL_0036: stloc.3
IL_0037: ldloc.3
IL_0038: brtrue.s IL_0041
IL_003a: ldloc.2
IL_003b: call void [mscorlib]System.Threading.Monitor::Exit(object)
IL_0040: nop
IL_0041: endfinally
} // end handler
IL_0042: nop
IL_0043: nop
IL_0044: ldloc.0
IL_0045: ldc.i4.1
IL_0046: add
IL_0047: stloc.0
IL_0048: ldloc.0
IL_0049: ldc.i4 0x3e8
IL_004e: clt
IL_0050: stloc.3
IL_0051: ldloc.3
IL_0052: brtrue.s IL_0005
IL_0054: ret
} // end of method '<>c__DisplayClass6'::'<TestMethod>b__3'</pre>
<p><strong>IL(Intermediate Language) </strong>koduna baktığımızda <strong>Monitor.Enter </strong>ve <strong>Monitor.Exit </strong>metod çağrılarının gerçekleştirildiği görülmektedir. Üstelik <strong>lock </strong>ifadesi içerisine alınan kod kısmı için arka planda bir <strong>try…finally </strong>bloğu oluşturulmuştur. <strong>Finally</strong> bloğunda gerçekleştirilen <strong>Monitor.Exit</strong> çağrısı, tahmin edileceği üzere her ne olursa olsun devreye girecektir. Tabi ki <strong>Monitor</strong> tipinin bilinçli olarak kullanılması gerektiği durumlarda söz konusu olabilir. Bu ve diğer durumları ilerleyen yazıları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%2fSynchronizationPrimitives.rar">SynchronizationPrimitives.rar (22,26 kb)</a><strong> [Örnek Visual Studio 2010 Ultimate sürümünde geliştirilmiş ve test edilmiştir]</strong></p>2011-01-27T13:40:00+00:00tpltask parallel library.net framework 4.0synchronization primitivesparallel programmingbsenyurtTPL ve Shared Data Isolation başlıklı yazımızda, n sayıda Task örneğinin ortaklaşa kullandıkları bir veri alanı üzerindeki işlemlerinin, ne gibi sonuçlara yol açabileceğini incelemiş ve bunun önün geçmek için basit bir kaç yolu ele almıştık.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=942810d0-cd14-4916-b0e3-823b4089ea470https://www.buraksenyurt.com/trackback.axd?id=942810d0-cd14-4916-b0e3-823b4089ea47https://www.buraksenyurt.com/post/TPL-Senkronizasyonu-Saglamak#commenthttps://www.buraksenyurt.com/syndication.axd?post=942810d0-cd14-4916-b0e3-823b4089ea47https://www.buraksenyurt.com/post/AttachedToParent-Hakkinda-DetaylarAttachedToParent Hakkında Detaylar2010-09-02T08:30:00+00:00bsenyurt<p><img style="float: right;" src="/pics/2010%2f4%2fblg178_Giris.jpg" alt="" />Merhaba Arkadaşlar,</p>
<p>Malum <strong>"her yiğidin farklı bir yoğurt yiğiş tarzı vardır"</strong> derler. Genellikle programlama dilleri veya<strong> .Net Framework</strong> gibi yapılarda da bir sonuca ulaşmak için birden fazla ve farklı yol söz konusu olabilir. Böyle bir durumun oluşmasına neden olan etkenlerin başında, çevresel ortam parametrelerinin farklılaşmasının geldiğini ifade edebiliriz.</p>
<p>Çok basit bir kaç örnek vererek olayı kafamızda daha net bir şekilde canlandırmaya çalışalım. Bir koleksiyon içerisindeki öğeleri for veya foreach döngüleri ile dolaşabiliriz. Ya da örneğin bir veri tablosundan veriyi çekmek için, DataTable bazlı bir tekniği veya DataReader bazlı bir yöntemi ele alabiliriz. Ancak öyle vakalar söz konusudur ki, aynı amaç için ele alınabilecek veya değerlendirilebilecek yolların sayısı çok fazladır. Bu fazlalık bir süre sonra karar vermeyi zorlaştırır ve işin içinden çıkılmaz bir duruma düşülebilir.</p>
<p>Söz gelimi <strong>paralel programlama</strong> konusu ile ilgili olarak bir süredir incelediğimiz <strong>Parent-Child Task</strong> ilişkilerini göz önüne alalım. Daha önceki iki yazımızda(<a href="https://www.buraksenyurt.com/post/Parent-Child-Tasks-Kavrami">Parent-Child Tasks Kavramı</a>, <a href="https://www.buraksenyurt.com/post/Parent-Child-Task-Exception-Durumlari" target="_blank">Parent-Child Task Exception Durumlar</a>) sürekli olarak <strong>AttachedToParent </strong>metodunun belirli bir kullanımını ele aldık. Oysa ki, <strong>Child Task </strong>örneklerinin <strong>Parent Task </strong>örneklerinin yaşam döngülerine eklenmelerinde izlenebilecek birden fazla yol bulunmaktadır. Buna göre <strong>Parent Task </strong>örneğine dahil olmak için aşağıdaki tekniklerden herhangibirisinden yararlanılabilir.</p>
<ul>
<li><strong>Task </strong>nesnesine ait <strong>yapıcı metod(Constructor) </strong>içerisinde <strong>TaskCreationOptions.AttachedToParent enum </strong>sabiti bildirimi yapılarak</li>
<li><strong>Task </strong>sınıfının <strong>static StartNew </strong>metodunda <strong>TaskCreationOptions.AttachedToParent enum </strong>sabiti bildirimi yapılarak</li>
<li><strong>Task </strong>nesne örneği üzerinden çağırılabilen <strong>ContinueWith </strong>metoduna <strong>TaskContinuationOptions.AttachedToParent enum </strong>sabiti parametresini geçirerek</li>
<li><strong>Task.Factory.ContinueWhenAll static </strong>metoduna <strong>TaskContinuationOptions</strong><strong>.AttachedToParent enum </strong>sabiti parametresini geçirerek</li>
<li><strong>Task.Factory.FromAsync</strong> metodu içerisinde <strong>TaskCreationOptions.AttachedToParent enum</strong> sabiti bildirimi yapılarak</li>
<li><strong>TaskCompletionSource </strong>nesne örneğini oluştururken, <strong>TaskCreationOptions.AttachedToParent enum </strong>sabitini parametre olarak geçirerek</li>
</ul>
<p>Yazımızın bundan sonraki bölümlerinde söz konusu çalıştırma seçeneklerinden bazılarını, örnek kodlar yardımıyla incelemeye çalışalım. İlk olarak aşağıdaki kod parçası ile işe başlayabiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Threading;
using System.Threading.Tasks;
namespace AttachedToParentCases
{
class Program
{
static void Main(string[] args)
{
Task parentTask = Task.Factory.StartNew(() =>
{
Console.WriteLine("Parent Task...");
#region 1 - Constructor Kullanımı
Task childTask1 = new Task(() =>
{
Console.WriteLine("Child Task 1");
}, TaskCreationOptions.AttachedToParent
);
childTask1.Start();
#endregion
Thread.Sleep(30000); //Debug modda Parallel Task' leri izlemek için konulmuştur
});
parentTask.Wait();
}
}
}</pre>
<p>Bu kod parçasında yer alan işleyişi kavrayabilmek <strong>childTask1 </strong>içerisindeki <strong>Console.Writeline </strong>satırına <strong>BreakPoint </strong>koyarak ilerleyecek ve çalışma zamanında <strong>Parallel Tasks </strong>penceresindeki durumu analiz etmeye çalışacağız. Bu işlemleri diğer kod örnekleri için de tekrar edeceğiz. İşte ilk kod parçamızın debug moddaki durumu.</p>
<p><img src="/pics/2010%2f4%2fblg178_Region1Runtime.gif" alt="" /></p>
<p>Bu kod örneğinde, <strong>parentTask </strong>nesne örneği <strong>StartNew </strong>metodu ile başlatılırken içerisinde de bir <strong>Child Task </strong>örneği önce <strong>new </strong>operatörü ile oluşturulmakta, sonrasında ise <strong>Start </strong>metodu ile çalıştırılmaktadır. Çalışma zamanında <strong>BreakPoint </strong>koyduğumuz noktadan <strong>Parallel Tasks </strong>penceresine baktığımızda iki adet <strong>Task </strong>örneğinin var olduğunu görebiliriz. <strong>parentTask </strong>nesne örneği <strong>Thread.Sleep </strong>metodu nedeniyle <strong>Waiting </strong>modundadır. Diğer yandan başlatılan <strong>Child Task </strong>örneği <strong>Running </strong>modundadır. Ancak burada önemli olan nokta <strong>ID </strong>değeri <strong>2 </strong>olan <strong>Task </strong>örneğinin <strong>Parent </strong>değeridir. Dikkat edileceği üzere <strong>2 </strong>numaralı <strong>ID </strong>değerine sahip <strong>Task </strong>örneğinin bağlı olduğu <strong>Task</strong>, <strong>1 </strong>numaralı <strong>ID </strong>değerine sahip <strong>Task </strong>örneğidir.</p>
<p>Gelelim ikinci kod örneğimize. Bu sefer <strong>Child Task </strong>örneğini <strong>Task.Factory.StartNew </strong>metodu yardımıyla oluşturmaktayız.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Threading;
using System.Threading.Tasks;
namespace AttachedToParentCases
{
class Program
{
static void Main(string[] args)
{
Task parentTask = Task.Factory.StartNew(() =>
{
Console.WriteLine("Parent Task...");
#region 2 - StartNew Kullanımı
Task childTask2 = Task.Factory.StartNew(() =>
{
Console.WriteLine("Child Task 2");
}, TaskCreationOptions.AttachedToParent
);
#endregion
Thread.Sleep(30000); //Debug modda Parallel Task' leri izlemek için konulmuştur
});
parentTask.Wait();
}
}
}</pre>
<p>Gelelim çalışma zamanı çıktısına.</p>
<p><img src="/pics/2010%2f4%2fblg178_Region2Runtime.gif" alt="" /></p>
<p>Bir önceki örnektekine benzer olaraktan, <strong>Child Task </strong>nesne örneğinin icra ettiği kod satırında durulduğunda, çalışmakta olan <strong>2 </strong>numaralı <strong>ID </strong>değerine sahip <strong>Task </strong>nesne örneğinin dahil olduğu <strong>Parent Task'</strong> in<strong>, </strong><strong>1 </strong>numaralı <strong>ID </strong>değerine sahip <strong>Task </strong>olduğu görülebilmektedir ki bu kodumuzda yer alan <strong>parentTask </strong>değişkeninin işaret ettiği <strong>Task</strong>' dir.</p>
<p>İlk iki kod örneğimizde olaylar oldukça nettir ve beklediğimiz şekildedir. Dilerseniz diğer örnekler ile devam edelim ve işleri biraz daha karıştıralım <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /> İşte yeni kod parçamız.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Threading;
using System.Threading.Tasks;
namespace AttachedToParentCases
{
class Program
{
static void Main(string[] args)
{
Task parentTask = Task.Factory.StartNew(() =>
{
Console.WriteLine("Parent Task...");
#region 3 - Task<Task>.Factory.StartNew() Kullanımı
Task<Task> childTask3 = Task<Task>.Factory.StartNew(() =>
{
Console.WriteLine("Child Task 3");
return Task.Factory.StartNew(() =>
{
Console.WriteLine("Child Task 4");
});
}
, TaskCreationOptions.AttachedToParent
);
#endregion
Thread.Sleep(30000); //Debug modda Parallel Task' leri izlemek için konulmuştur
});
parentTask.Wait();
}
}
}</pre>
<p>Bu sefer biraz daha dikkatli davranmamız gerekiyor. <strong>childTask3 </strong>isimli nesne örneği oluşturulurken, içerisinde iş yapan diğer bir <strong>Child Task</strong> başlatılmaktadır. Dikkat edileceği üzere <strong>childTask3 </strong>için <strong>AttachedToParent </strong>değeri kullanılmış ancak içerideki <strong>childTask4 </strong>için böyle bir bildirimde bulunulmamıştır. Söz konusu yeni kod parçası çalışma zamanında debug edilirken iki noktada durup düşünmek gerekmektedir.</p>
<p><img src="/pics/2010%2f4%2fblg178_Region3_1Runtime.gif" alt="" /></p>
<p>Yukarıdaki duruma göre <strong>childTask3</strong>, <strong>parentTask</strong>' in alt <strong>Task </strong>örneğidir. <strong>Parent </strong>sütünundaki <strong>1</strong> değeri bunu ispat etmektedir. İlginç olan ise <strong>childTask3 </strong>içerisinde başlatılan yeni bir <strong>Task</strong>' in içerisindeki <strong>BreakPoint </strong>noktasında durulduğunda ortaya çıkmaktadır.</p>
<p><img src="/pics/2010%2f4%2fblg178_Region3_2Runtime.gif" alt="" /></p>
<p>Volaaa!!! <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /> Dikkat edilecek olursa en içteki <strong>Task</strong>, <strong>parentTask </strong>nesne örneğinin çalışma zamanındaki yaşam döngüsüne ilave <span style="text-decoration: underline;">edilmemiştir</span>. Kendi başına çalışan bir <strong>Task </strong>olarak ele alınmaktadır. İşte bu, dikkat edilmesi gereken vakalardan birisidir. Nitekim parent <strong>Task </strong>örneğine <strong>Attach </strong>edilen bir <strong>Task </strong>içerisindeki <strong>Task</strong>' lerin <strong>enum </strong>sabitinin ilgili değeri belirtilmeden <strong>Attach </strong>olmaları gerektiği sanılabilir. Oysaki şu durumda böyle olmadığı görülmektedir.</p>
<p>Gelelim 4ncü kod parçamıza.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Threading;
using System.Threading.Tasks;
namespace AttachedToParentCases
{
class Program
{
static void Main(string[] args)
{
Task parentTask = Task.Factory.StartNew(() =>
{
Console.WriteLine("Parent Task...");
#region 4 - ContinueWith Kullanımı
Task detachedTask = Task.Factory.StartNew(() =>
{
Console.WriteLine("Detached Task");
}
);
Task childTask5 = detachedTask.ContinueWith((t) =>
{
Console.WriteLine("Child Task 5");
}
, TaskContinuationOptions.AttachedToParent);
#endregion
Thread.Sleep(30000); //Debug modda Parallel Task' leri izlemek için konulmuştur
});
parentTask.Wait();
}
}
}</pre>
<p>Bu kez <strong>ContinueWith </strong>kullanımı söz konusudur. Dikkat edileceği üzere <strong>Parent Task </strong>örneğine <strong>Attach </strong>edilmeyen <strong>detachedTask </strong>isimli bir <strong>Task </strong>örneği mevcuttur. Lakin <strong>childTask5 </strong>isimli nesne örneği oluşturulurken <strong>ContinueWith </strong>metodu kullanılmış ve ayrıca <strong>TaskContinuationOptions.AttachedToParent enum </strong>sabiti ile <strong>parent Task </strong>örneğine <strong>Attach </strong>edileceği belirlenmiştir. Bakalım gerçekten böyle midir? Yine iki noktada <strong>BreakPoint </strong>kullanarak söz konusu durumu analiz etmeye çalışacağız. İlk olarak <strong>detachedTask </strong>içerisinde duralım.</p>
<p><img src="/pics/2010%2f4%2fblg178_Region4_1Runtime.gif" alt="" /></p>
<p>Görüldüğü gibi <strong>detachedTask </strong>örneği açıkça belirtilmediği için <strong>Parent Task </strong>örneğinin yaşam döngüsüne dahil edilmemiştir ki normali de buduru. Ancak ikinci <strong>BreakPoint </strong>noktasına geldiğimizde aşağıdaki ekran görüntüsünde yer alan sonuçlar ile karşılaşırız.</p>
<p><img src="/pics/2010%2f4%2fblg178_Region4_2Runtime.gif" alt="" /></p>
<p>Beklediğimiz gibi <strong>childTask5 </strong>nesne örneği <strong>1 </strong>numaralı <strong>ID </strong>değerine sahip <strong>parentTask </strong>nesne örneğinin başlattığı yaşam döngüsüne dahil edilmiştir. Dikkat edilmesi gereken nokta, <strong>Attach </strong>edilmeyen bir <strong>Task </strong>örneği ile devam eden başka bir <strong>Task </strong>örneğinin, <strong>Parent Task </strong>yaşam döngüsüne dahil edilebiliyor olmasıdır.</p>
<p>5nci durum ile devam edelim.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Threading;
using System.Threading.Tasks;
namespace AttachedToParentCases
{
class Program
{
static void Main(string[] args)
{
Task parentTask = Task.Factory.StartNew(() =>
{
Console.WriteLine("Parent Task...");
#region 5 - ContinueWhenAll Kullanımı
Task detachedTask2 = Task.Factory.StartNew(() =>
{
Console.WriteLine("Detached Task 2");
}
);
Task childTask6 = Task.Factory.ContinueWhenAll(new Task[] { detachedTask2 }, (t) =>
{
Console.WriteLine("Child Task 6");
}, TaskContinuationOptions.AttachedToParent);
#endregion
Thread.Sleep(30000); //Debug modda Parallel Task' leri izlemek için konulmuştur
});
parentTask.Wait();
}
}
}</pre>
<p>Bu kez yine <strong>Detached </strong>olarak tesis edilmiş bir <strong>Task </strong>örneği söz konusudur. <strong>Parent Task </strong>içerisine ilave etme işlemi için ise, <strong>ContinueWhenAll </strong>metodundan yararlanılmaktadır. Bir önceki örneğimizde olduğu gibi iki <strong>BerakPoint</strong> ile ilerlememizde yarar vardır. İşte sonuçlar.</p>
<p><img src="/pics/2010%2f4%2fblg178_Region5_1Runtime.gif" alt="" /></p>
<p>Beklendiği üzere <strong>detachedTask2 </strong>kesinlikle <strong>Parent Task</strong> nesne örneğinin başlattığı yaşam döngüsüne dahil <span style="text-decoration: underline;">edilmemiştir</span>. Ancak bu durum <strong>childTask6 </strong>nesne örneği için geçerli değildir.</p>
<p><img src="/pics/2010%2f4%2fblg178_Region5_2Runtime.gif" alt="" /></p>
<p>Gelelim bir diğer kod parçamıza. Bu biraz ilginç bir deneyim olacak aslında <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Threading;
using System.Threading.Tasks;
namespace AttachedToParentCases
{
class Program
{
static void Main(string[] args)
{
Task parentTask = Task.Factory.StartNew(() =>
{
Console.WriteLine("Parent Task...");
#region 6 - FromAsync
Task detachedTask3 = Task.Factory.StartNew(() =>
{
Console.WriteLine("detached task 3");
}
);
Task childTask7 = Task.Factory.FromAsync(detachedTask3, (iar) =>
{
Console.WriteLine("Child Task 7");
}, TaskCreationOptions.AttachedToParent);
#endregion
Thread.Sleep(30000); //Debug modda Parallel Task' leri izlemek için konulmuştur
});
parentTask.Wait();
}
}
}</pre>
<p>Söz konusu örnekte takip ettiğim kaynakların belirttiğine göre, <strong>childTask7 </strong>örneğinin <strong>Parent Task </strong>örneğine <strong>Attach </strong>olması beklenmektedir. Bakalım gerçekten böyle midir? Yine iki noktada <strong>BreakPoint </strong>koyarak çalışma zamanındaki durumu incelememizde yarar vardır. İşte ilk durum;</p>
<p><img src="/pics/2010%2f4%2fblg178_Region6_1Runtime.gif" alt="" /></p>
<p>Beklediğimiz gibi <strong>detachedTask3 </strong>nesne örneği, <strong>Parent Task</strong> örneğinin yaşam döngüsüne ilave edilmemiştir. Kodun ilerleyen kısımlarında <strong>childTask7 </strong>nesne örneği üretilmeye çalışılmış ve bu amaçla <strong>FromAsync </strong>metodundan yararlanılmıştır. Metodun ilk parametresi <strong>detachedTask3 </strong>isimli nesne örneğidir. İkinci parametre olarak <strong>IAsyncResult </strong>arayüzü tipinden bir referansı parametre olarak alan <strong>isimsiz metod(AnonymousMethod) </strong>söz konusudur ve son parametre ile üretilen <strong>Task </strong>örneğinin <strong>Parent Task </strong>örneğine <strong>Attach </strong>edilmesi istenmektedir. Oysaki ikinci <strong>BreakPoint </strong>noktasında durum aşağıdaki gibidir.</p>
<p><img src="/pics/2010%2f4%2fblg178_Region6_2Runtime.gif" alt="" /></p>
<p><strong>3</strong> numaralı <strong>ID </strong>değerine sahip olan <strong>Task, childTask7 </strong>nesne örneğini işaret etmektedir ve <strong>Parent </strong>değeri yoktur. Bir başka deyişle bu <strong>Task </strong>örneği, herhangibir <strong>Task</strong> örneğinin<em>(Özellikle parentTask değişkeninin başlattığı)</em> yaşam döngüsüne <span style="text-decoration: underline;">katılmamıştır</span>. Bu durumu bende biraz garipsemiş durumdayım ve araştırmalarıma devam etmekteyim. Mutlaka gözden kaçırdığım bir yer olmalı diye düşünüyorum. Belki de siz bana bu konuda yardımcı olabilirsiniz. Nitekim şu anda benim de bir <strong>BreakPoint </strong>anında belirli bir süre beklemem gerekiyor. <img title="Smile" src="/editors/tiny_mce3/plugins/emotions/img/smiley-smile.gif" alt="Smile" border="0" /></p>
<p>Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2010%2f9%2fAttachedToParentCases.rar">AttachedToParentCases.rar (26,72 kb)</a><strong> [Örnek Visual Studio 2010 Ultimate sürümü üzerinde geliştirilmiş ve test edilmiştir]</strong></p>2010-09-02T08:30:00+00:00tpltask parallel libraryparallel programmingparallel computingbsenyurtDaha önceki iki yazımızda(Parent-Child Tasks Kavramı, Parent-Child Task Exception Durumlar) sürekli olarak AttachedToParent metodunun belirli bir kullanımını ele aldık. Oysa ki, Child Task örneklerinin Parent Task örneklerinin yaşam döngülerine eklenmelerinde izlenebilecek birden fazla yol bulunmaktadır. Buna göre Parent Task örneğine dahil olmak için aşağıdaki tekniklerden herhangibirisinden yararlanılabilir.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=cfbc1979-e691-4295-ae92-8c5557bfddd30https://www.buraksenyurt.com/trackback.axd?id=cfbc1979-e691-4295-ae92-8c5557bfddd3https://www.buraksenyurt.com/post/AttachedToParent-Hakkinda-Detaylar#commenthttps://www.buraksenyurt.com/syndication.axd?post=cfbc1979-e691-4295-ae92-8c5557bfddd3https://www.buraksenyurt.com/post/Parent-Child-Task-Exception-DurumlariParent-Child Task Exception Durumları2010-08-02T21:10:00+00:00bsenyurt<p><img style="float: right;" src="/pics/2010%2f4%2fblg174_Giris.jpg" alt="" />Merhaba Arkadaşlar,</p>
<p>Daha orta okul sıralarındayken havacılığa karşı müthiş bir ilgim vardı. Hiç unutmuyorum o yıllarda <strong>Uçan Türk </strong>dergisinin sıkı bir fanatiğiydim. Pek çok savaş uçağının teknik özelliklerini ezbere bilirdim ve hatta onları arşivlediğim bir not defterim dahi vardı. Uçmaktan korkan birisi olmama rağmen bunu yeneceğimi düşünerekten <strong>Lise </strong>yıllarında <strong>Hava Harp Okuluna </strong>girebilmek için özel bir çalışma programı bile uygulamıştım. Düzenli olarak spor yapıyor, kondisyon arttırmaya çalışıyor, günde değil 3, 5 kere dişlerimi fırçalıyor, gözlerimi yormamak için uykuma özen gösteriyordum.</p>
<p>Tabi öğrenciliğim çok parlak olmadığı için <strong>ÖSS </strong>sınavında aşmam gereken <strong>150</strong>' lik puan barajı konusunda tereddütler yaşıyordum. Nitekim barajı da geçemedim. Hayallerim yıkılmış mıydı? Elbette hayır. Heleki o yıllardaki çalışma azmimim bana kazandırdığı önemli avantajlar olduğu düşünüldüğünde. Bunlardan birisi de derin detaylara inebilmek için gerekli eforu, gayreti gösterme isteğidir. Neden böyle bir giriş yaptığıma gelince...Bu seferki konumuz <strong>Paralel Programlamada</strong>, ilişkisel <strong>Task </strong>örneklerinin <strong>Exception </strong>yönetimi hakkındadır. Konu sıkıcı ve bir o kadarda detaylıdır. Ama neyseki araştırıp, sıkılmadan derinlerine inmek ve analiz etmek için gerekli gayret mevcut <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /></p>
<p>Hatırlayacağınız üzere <a class="postheader taggedlink" href="https://www.buraksenyurt.com/admin/app/editor/post/Parent-Child-Tasks-Kavrami" target="_blank">Parent-Child Tasks Kavramı</a> başlıklı yazımızda <strong>.Net Framework 4.0</strong> tarafında paralel programlamada önemli bir yere sahip olan <strong>Task </strong>örnekleri arasındaki <strong>Parent</strong>, <strong>Child</strong> ilişkiyi incelemeye çalışmıştık.<strong> Parent-Task</strong> nesne örnekleri arasındaki ilişkilerde bilinmesi gereken konulardan birisi de, istisnaların nasıl ele alındığıdır<strong>(Exception Handling)</strong>. Aslında konuya hızlı bir giriş yaparak ilerlememiz şu aşamada avantajımız olacaktır. Bu nedenle <strong>Visual Studio 2010 Ultimate</strong> ortamında geliştireceğimiz <strong>Console </strong>uygulamasında aşağıdaki kod içeriğinin yer aldığını göz önüne alalım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Data.SqlClient;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace ParentChildTasksExceptionHandling
{
class Program
{
static void Main(string[] args)
{
#region Case 1
Task parent = Task.Factory.StartNew(() =>
{
Console.WriteLine("Parent task...");
Task child1 = Task.Factory.StartNew(() =>
{
Console.WriteLine("Child Task 1 başladı...");
Thread.Sleep(5000);
Console.WriteLine("Child Task 1 bitti...");
}
, TaskCreationOptions.AttachedToParent);
Task child2 = Task.Factory.StartNew(() =>
{
Console.WriteLine("Child Task 2 başladı...");
FileStream stream=File.OpenRead("OlmayanDosya.txt");
Console.WriteLine("Child Task 2 bitti...");
}
, TaskCreationOptions.AttachedToParent);
Task child3 = Task.Factory.StartNew(() =>
{
Console.WriteLine("Child Task 3 başladı...");
Thread.Sleep(3000);
Console.WriteLine("Child Task 3 bitti...");
}
, TaskCreationOptions.AttachedToParent);
Task child4 = Task.Factory.StartNew(() =>
{
Console.WriteLine("Child Task 4 başladı...");
Thread.Sleep(10000);
SqlConnection conn = new SqlConnection();
conn.Open();
Console.WriteLine("Child Task 4 bitti...");
}
, TaskCreationOptions.AttachedToParent);
}
);
try
{
Console.WriteLine("İşlemler yürütülüyor");
parent.Wait();
Console.WriteLine("İşlemler tamamlandı");
}
catch(AggregateException excp)
{
Console.WriteLine("Parent task durumu {0}",parent.Status);
Console.WriteLine(excp.Message);
foreach (var innerExcp in excp.InnerExceptions)
{
Console.WriteLine(innerExcp.InnerException.Message);
}
}
#endregion
}
}
}</pre>
<p>Buradaki kod parçasında bir <strong>Parent Task</strong> ve bunun içerisinde çalışacak şekilde planlanan 4 farklı <strong>Child Task </strong>örneği yer almaktadır. Bu örneklerden birisi, olmayan bir dosyayı açmaya çalışmaktadır. Dolayısıyla <strong>FileNotFoundException </strong>tipinden bir istisna nesnesi üreteceği garantidir. Diğer bir <strong>Task </strong>ise parametresiz bir <strong>SqlConnection </strong>nesnesi oluşturmakta ve belli olmayan bir yere doğru <strong>SQL</strong> bağlantısı açmaya çalışmaktadır ki bu da <strong>InvalidOperationException </strong>türünden bir istisna nesnesinin fırlatılmasına neden olacaktır.</p>
<p>Daha önceki yazımızdan hatırlayacağınız üzere <strong>Child Task</strong> örneklerinin başlattığı metod gövdeleri içerisinde oluşabilecek olan <strong>istisnalar(Exception), Parent Task </strong>örneğinin<strong> Final State</strong> durumunu da doğrudan etkilemektedir. Ayrıca <strong>Child Task</strong>' lerde bir istisna oluşsa bile, diğer <strong>Task </strong>örnekleri çalışmalarını devam ettirmektedir. Örnek kod parçamızda <strong>Parent Task </strong>tarafına çıkartılan <strong>Exception </strong>örneklerinin yakalanabilmesi amacıyla <strong>try...catch </strong>bloğuna başvurulduğu görülmektedir.</p>
<p>Burada dikkat edilmesi gereken iki nokta vardır. Bunlardan birisi <strong>try </strong>bloğu içerisinde <strong>Parent Task </strong>nesne örneği üzerinden bilinçli olarak <strong>Wait </strong>metodunu kullanılmış olmasıdır. Bu şekilde uygulamanın ana <strong>Thread </strong>bloğu sonlanmadan <strong>Parent Task </strong>ve içeriğinin işlerinin tamamlanmasının beklenmesi garanti edilmiş olmaktadır. Diğer yandan ikinci önemli nokta <strong>catch </strong>bloğu içerisinde, <strong>Parent Task </strong>örneği altında çalışan <strong>Child Task</strong>' ler tarafından fırlatılan <strong>Exception </strong>nesnelerinin nasıl yakalandığıdır. Burada <strong>AggregateException </strong>sınıfının <strong>InnerExceptions </strong>isimli koleksiyonu üzerinden hareket edildiğine dikkat edilmelidir. Uygulama kodu <strong>Debug </strong>modda çalıştırıldığında aşağıdaki sonuçların elde edildiği görülecektir.</p>
<p><img src="/pics/2010%2f4%2fblg174_DebugException.gif" alt="" /></p>
<p>Görüldüğü gibi <strong>AggregateException </strong>nesnesi <strong>Child Task</strong>' lerde oluşan istisnaları bir koleksiyon dahilinde saklamaktadır. <strong>Count </strong>değerinin iki dönmesinin sebebi tahmin edeceğiniz üzere iki <strong>Child Task</strong>' in <strong>Exception </strong>fırlatmış olmasıdır. Uygulamamızın çalışma zamanı görüntüsü ise aşağıdaki gibi olacaktır.</p>
<p><img src="/pics/2010%2f4%2fblg174_RuntimeCase1.gif" alt="" /></p>
<p>Dikkat edileceği üzere <strong>Parent Task</strong> örneğinin<strong> Final State</strong> durumundaki karşılığı <strong>Faulted </strong>olmuştur ki bu son derece doğaldır. Çünkü <strong>Child Task </strong>örneklerinden ikisi <strong>Exception </strong>üretimi bildirmiştir. Bu istisna bildirimlerinin <strong>Child Task</strong>' lerden, <strong>Parent Task</strong>' e bildirildiğini ve <strong>AggregateException </strong>içerisinde toplandıklarını da unutmamak gerekir.</p>
<p><strong>Exception </strong>yönetimi ile ilişkili tek durum bu değildir. Şimdi de aşağıdaki kod örneğini göz önüne alalım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Data.SqlClient;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace ParentChildTasksExceptionHandling
{
class Program
{
static void Main(string[] args)
{
#region Case 2
Task parentTask = null;
Task childTask = null;
ManualResetEvent mre = new ManualResetEvent(false);
parentTask = Task.Factory.StartNew(() =>
{
childTask = Task.Factory.StartNew(() =>
{
throw new Exception("Child Task için Exception");
}, TaskCreationOptions.AttachedToParent);
mre.Set();
throw new Exception("Parent Task için Exception");
}
);
mre.WaitOne();
try
{
//Task.WaitAll(parentTask, childTask);
parentTask.Wait();
}
catch (AggregateException excp)
{
Console.WriteLine("{0} adet Exception söz konusudur",excp.InnerExceptions.Count);
}
#endregion
}
}
}</pre>
<p>Bu kod parçasında iki adet <strong>Task </strong>örneği bulunmaktadır ve aralarında <strong>Parent-Child</strong> ilişki söz konusudur. Dikkat edilmesi gereken nokta ise, kodun bu haliyle <strong>debug </strong>edilmesi veya çalıştırılması sonrasında <strong>AggregateException</strong> nesne örneği üzerinden yaklanan dahili istisna örnekleri ve toplam sayısıdır. <strong>parentTask.Wait(); </strong>metod çağrısı kullanıldığında <strong>debug </strong>modda iken aşağıdaki sonuçlar ulaşılabildiğim gözlemlenecektir.</p>
<p><img src="/pics/2010%2f4%2fblg174_RuntimeCase2_2.gif" alt="" /></p>
<p>Aslında beklediğimiz gibi bir sonuç söz konusudur. <strong>parentTask </strong>nesne örneği üzerinden <strong>Wait </strong>metodu kullanıldığı için <strong>AggregateException </strong>nesne örneğinin <strong>InnerExceptions </strong>koleksiyonunun ilk elemanı <strong>Parent Task </strong>örneğinin çalıştırdığı metod içerisinden fırlatılan istisna bilgisini içermektedir. <strong>InnerExceptions </strong>özelliğinin 1 numaralı indis değeri içinse, <strong>Child Task</strong> içerisinden üretilen <strong>Exception </strong>söz konusudur. Ancak hem <strong>Parent Task</strong> hemde içerdiği <strong>Child Task </strong>nesne örnekleri için <strong>Wait </strong>işlemi gerçekleştirilirse? <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" />Yani yorum satırı açılıp <strong>Task.WaitAll(parentTask,childTask)</strong> kullanılırsa, bu durumda <strong>Debug </strong>modda aşağıdaki sonuçlar ile karşılaşırız.</p>
<p><img src="/pics/2010%2f4%2fblg174_RuntimeCase2New.gif" alt="" /></p>
<p>Dikkat edileceği üzere <strong>AggregateException, 3 </strong>adet <strong>InnerException </strong>içerdiğini bildirmektedir. Her ne kadar <strong>throw new </strong>satırlarının sayısı iki olsa da <strong>3 </strong>sonuç döndürülmüştür. Yakından bakıldığında ise durum aslında şu şekilde özetlenebilir; <strong>AggregateException, WaitAll </strong>çağrısı nedeniyle ne kadar <strong>Task </strong>örneği varsa bunların tamamının ürettiği <strong>Exception </strong>bilgilerini<em>(parentTask' in ürettiği ve yukarıya gönderdiği Exception dahil) </em><strong>InnerExceptions</strong> altında toplamıştır.</p>
<p>Bunlara ilaveten<strong> Parent Task</strong> örneği içerisindeki <strong>Child Task </strong>örneğinden fırlatılan <strong>Exception, </strong>ayrıca <strong>InnerExceptions </strong>içerisindeki <strong>2 </strong>numaralı indise atanmıştır. Dolayısıyla birden fazla <strong>Task </strong>örneğinin <strong>WaitAll </strong>ile beklenmesi halinde <strong>AggregateException </strong>nesnesinin istisna toplama yaklaşımı, sadece <strong>Parent Task </strong>örneğine uygulanan <strong>Wait </strong>metodu söz konusu olduğundakinden farklıdır. Bu çok tabi olarak üst tarafta Exception örneklerinin yakalanıp değerlendirildiği konumlarda önem kazanan küçük bir farktır.</p>
<p>Şu ana kadar yaptıklarımızı değerlendirdiğimizde <strong>Child Task</strong> tamamlandığında eğer bir <strong>Exception </strong>içeriyorsa bunu <strong>Parent Task</strong>' e bildirdiği yönündedir.</p>
<p><strong>Exception </strong>nesnelerinin ele alınması ile ilişkili bir diğer yaklaşımı ise aşağıdaki kod parçasından devam ederek değerlendirebiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Threading;
using System.Threading.Tasks;
namespace ParentChildTasksExceptionHandling
{
class Program
{
static void Main(string[] args)
{
#region Case 3
Task parentTask = null;
Task childTask = null;
parentTask = Task.Factory.StartNew(() =>
{
childTask = Task.Factory.StartNew(() =>
{
throw new Exception("Child Task için Exception");
}, TaskCreationOptions.AttachedToParent);
try
{
childTask.Wait();
}
catch
{
}
}
);
try
{
parentTask.Wait();
Console.WriteLine("Parent Task Status : {0}",parentTask.Status);
}
catch (AggregateException excp)
{
Console.WriteLine("{0} adet Exception söz konusudur", excp.InnerExceptions.Count);
}
#endregion
}
}
}</pre>
<p>Bu sefer <strong>Parent Task</strong> için açılan kod içerisinde <strong>Child Task</strong> örneği için <strong>Wait </strong>metodu çağrısı yapılmış ve söz konusu çağrı sırasında bir <strong>Exception </strong>oluşrsa, boş bir catch bloğunda yakalanmıştır. Dikkat çekici nokta da zaten burasıdır. Boş <strong>catch </strong>bloğu içerisinde herhangibir şekilde <strong>Exception </strong>nesne örneği fırlatılmadığından,<strong> Child Task</strong> tarafından üretilen istisna, <strong>Parent Task</strong> örneğine bildirilmemektedir. Bir başka deyişle Child Task örneğinin ürettiği istisna kendi içerisinde ele alınarak süpürülmüştür. Dolayısıyla bu örnek kod parçasının çalışma zamanı çıktısı aşağıdaki gibi olacak, bir başka deyişle, <strong>Parent Task</strong> nesne örneğinin <strong>Final State </strong>durumu <strong>RanToCompletion</strong> olarak belirlenecektir.</p>
<p><img src="/pics/2010%2f4%2fblg174_RuntimeCase3.gif" alt="" /></p>
<p>Şimdi burada durup önemli bir noktayı vurgulamak gerektiği düşüncesindeyim. <strong>WaitAll </strong>metodunu kullandığımız örnekte<strong> Child Task</strong> ve<strong> Parent Task</strong> örneklerinin durumları <strong>Faulted </strong>olarak set edilmektedir. Ancak son senaryoda <strong>Parent Task</strong> örneğine <strong>Child Task </strong>içerisinde fırlatılan <strong>Exception </strong>bildirilmediğinden sadece<strong> Child Task</strong>, <strong>Faulted </strong>durumuna düşecek ve <strong>Parent Task</strong>, <strong>RanToCompletion </strong>modunda olacaktır. Bunun belirgin olan sebebi az öncede belirttiğimiz üzere, <strong>Child Task</strong> içerisinden fırlatılan istisnanın <strong>Parent Task</strong>' e çıkartılmadan bir<strong> try...catch </strong>bloğu ile kontrol altına alınmasıdır. Ancak yine son senaryoda aşağıdaki gibi bir kullanım ile <strong>Parent Task</strong>' e <strong>Child Task</strong>' ten fırlatılan <strong>Exception</strong> durumunun bildirilmesi sağlanabilir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Threading;
using System.Threading.Tasks;
namespace ParentChildTasksExceptionHandling
{
class Program
{
static void Main(string[] args)
{
#region Case 4
Task parentTask = null;
Task childTask = null;
parentTask = Task.Factory.StartNew(() =>
{
childTask = Task.Factory.StartNew(() =>
{
throw new Exception("Child Task için Exception");
}, TaskCreationOptions.AttachedToParent);
childTask.ContinueWith(_ =>
{
try
{
childTask.Wait();
}
catch(AggregateException excp)
{
}
}
);
}
);
try
{
parentTask.Wait();
Console.WriteLine("Parent Task Status : {0}", parentTask.Status);
}
catch (AggregateException excp)
{
Console.WriteLine("{0} adet Exception söz konusudur\nParent Task Durumu : {1}", excp.InnerExceptions.Count,parentTask.Status);
}
#endregion
}
}
}</pre>
<p>Bu kod parçasında<strong> Child Task</strong> nesne örneği üzerinden <strong>Continue </strong>metodu kullanılmış ve<strong> try...catch</strong> bloğu ile alt task örneğinin ürettiği istisnanın yine aynı blok içerisinde ele alınması sağlanmıştır. Bu durumda <strong>Parent Task</strong> yine <strong>Child Task</strong> içerisinden üretilen <strong>Exception </strong>nesnesi için bilgilendirilecektir, üstelik <strong>Continue </strong>bloğu içerisindeki <strong>catch </strong>bloğundan dışarıya doğru bir <strong>Exception </strong>fırlatımı açık bir şekilde yapılmasa bile. Tabi bu durumda <strong>Parent Task </strong>nesne örneğinin <strong>Final State </strong>durumu yine <strong>Faulted </strong>olacaktır. İşte çalışma zamanı çıktısı.</p>
<p><img src="/pics/2010%2f4%2fblg174_RuntimeCase4.gif" alt="" /></p>
<p>Görüldüğü gibi <strong>Parent-Child Task</strong> diyerek geçmemek lazım <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /> Ele alınması gereken bir kaç durum söz konusu ki bunlardan birisi de iptal <strong>işlemleri(Cancellation)</strong>. Bu konuyu da 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%2f8%2fParentChildTasksExceptionHandling.rar">ParentChildTasksExceptionHandling.rar (25,96 kb)</a><strong> [Örnek Visual Studio 2010 Ultimate sürümü üzerinde geliştirilmiş ve test edilmiştir]</strong></p>2010-08-02T21:10:00+00:00tpltask parallel libraryparallel programmingparallel computingbsenyurtHatırlayacağınız üzere Parent-Child Tasks Kavramı başlıklı yazımızda .Net Framework 4.0 tarafında paralel programlamada önemli bir yere sahip olan Task örnekleri arasındaki Parent, Child ilişkiyi incelemeye çalışmıştık. Parent-Task nesne örnekleri arasındaki ilişkilerde bilinmesi gereken konulardan birisi de, istisnaların nasıl ele alındığıdır(Exception Handling). Aslında konuya hızlı bir giriş yaparak ilerlememiz şu aşamada avantajımız olacaktır.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=0c36eb8e-d9f3-4e97-8962-90d027495a1a0https://www.buraksenyurt.com/trackback.axd?id=0c36eb8e-d9f3-4e97-8962-90d027495a1ahttps://www.buraksenyurt.com/post/Parent-Child-Task-Exception-Durumlari#commenthttps://www.buraksenyurt.com/syndication.axd?post=0c36eb8e-d9f3-4e97-8962-90d027495a1ahttps://www.buraksenyurt.com/post/TPL-Deadlock-Local-For-Variable-EvaluationsTPL – Göz Göre Göre Başımızı Belaya Sokmak2010-06-21T06:08:00+00:00bsenyurt<p><a href="https://www.buraksenyurt.com/pics/blg209_Giris.jpg"><img style="display: inline; margin-left: 0px; margin-right: 0px; border: 0px;" title="blg209_Giris" src="/pics/blg209_Giris_thumb.jpg" alt="blg209_Giris" width="304" height="228" align="right" border="0" /></a>Merhaba Arkadaşlar,</p>
<p>Bazen göz göre göre başımıza bi ton dert açarız. Kimi zaman başlayacağımız iş bize çok eğlenceli gelebilir <em>(Yandaki resimde yüzü görünmeyen şahsın da bu heyacanla Hamburgere bindiğinden eminiz)</em> Ama işin sonuçlarını biliyorsak eğer, bunu yapmamızın nedeni büyük olasılıkla adrenalindir.</p>
<p>Tabi ki bir yazılımcı için adrenalin genellikle üst yöneticisi tarafından salgılanan bir hormondur. Nitekim yazılımcıların, ilerideki felaketleri kestirerek hareket etmesi ve geliştirmeleri buna göre yapması her zaman kolay olmayabilir. Bir başka deyişle bazı vakalara hazırlıklı olmak için önceden bunları çalışmak gerekmektedir.</p>
<p>İşte bu yazımızda biz de<strong> Task Parallel Library</strong> için söz konusu olan ve geliştiricilerin başını derde sokacak 2 vaka üzerinde duruyor olacağız. Haydi o zaman parmakları sıvayalım ve işe koyulalım.</p>
<p><strong>Deadlock Durumu</strong></p>
<p>Bu kelime her zaman korkutucu olmuştur. Yazılım Geliştirme serüvenime ilk başladığım yıllarda çoğunlukla veritabanı tarafındaki kilitlenmelerden söz edildiğini çok net hatırlıyorum. Ancak birden fazla iş parçasının da <strong>deadlock’</strong> a düşmesi, bir başka deyişle birbirlerini beklemeleri nedeniyle, içinde çalıştıkları <strong>Thread</strong>’ i<em>(çoğunlukla ana uygulama iş parçası-<strong>Main Thread</strong>) </em>kitlemeleri söz konusudur. Durumu daha net anlayabilmek için 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.Threading.Tasks;
namespace Disasters
{
class Program
{
static void Main(string[] args)
{
Task<double> task1 = null;
Task<int[]> task2 = null;
task2 = Task.Factory.StartNew<int[]>(() =>
{
double task1Result=task1.Result;
Random rnd = new Random();
int[] numbers = new int[5];
for (int i = 0; i < 5; i++)
{
numbers[i] = rnd.Next(1, 250);
}
return numbers;
}
);
task1 = Task.Factory.StartNew<double>(()=>
{
double totalValue = 0;
foreach (int number in task2.Result)
{
totalValue += number;
}
return totalValue;
}
);
Task.WaitAll(task1, task2);
Console.WriteLine("İşlemlerin sonu.Programdan çıkmak için bir tuşa basınız");
Console.ReadLine();
}
}
}</pre>
<p>Aslında kod çok fazla değerlendirilebilir veya anlamlı değildir. Ancak <strong>Deadlock</strong> oluşumunu görmemiz açısından yeterlidir. Örnekte <strong>task1</strong> ve <strong>task2</strong> isimli <strong>Task</strong> nesne örneklerinin, birbirlerinin dönüş değerlerini kullanmaya çalıştığı ifade edilmektedir. İşte <strong>Task</strong> örneklerinin çalışma zamanında birbirlerini beklemeleri, kendi durumlarının <strong>Deadlock</strong> olarak set edilmesine neden olacaktır. Bu durum <strong>Debug</strong> modda aşağıdaki ekran görüntüsünde olduğu gibi görülebilir.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg209_Debug1.gif"><img style="display: inline; border: 0px;" title="blg209_Debug1" src="/pics/blg209_Debug1_thumb.gif" alt="blg209_Debug1" width="644" height="105" border="0" /></a></p>
<p>Görüldüğü üzere her iki <strong>Task</strong> birbirini bekler şekilde kalmıştır. Çok doğal olarak çalışma zamanı çıktısı kapkara bir ekran olacaktır.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg209_FirstRuntime.gif"><img style="display: inline; border: 0px;" title="blg209_FirstRuntime" src="/pics/blg209_FirstRuntime_thumb.gif" alt="blg209_FirstRuntime" width="533" height="170" border="0" /></a></p>
<p>Gelelim diğer bir senaryoya.</p>
<p><strong>Döngü Değişkenlerine Dikkat</strong></p>
<p>Bu aslında oldukça eğlenceli ve bir o kadarda beklenmedik sonuçları üreten vakalardandır. Olayı hızlı bir şekilde değerlendirmek adına 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.Tasks;
namespace Disasters
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
Task.Factory.StartNew(() =>
{
for (int j = 0; j < 125000000; j++)
{
j++;
j--;
j*=j;
}
Console.WriteLine("Güncel Task Id : {0}\tGüncel i : {1}",Task.CurrentId,i.ToString());
}
);
}
Console.WriteLine("Kapatmak için bir tuşa basınız");
Console.ReadLine();
}
}
}</pre>
<p>Örnek kod ile çalışma zamanında <strong>10</strong> <strong>Task</strong> örneği başlatılmakta ve bunların <strong>lambda</strong> <strong>ifadeleri(=> Expressions)</strong> içerisinden o anki<strong> i</strong> değerleri ekrana yazdırılmaktadır. Normal şartlarda<strong> i</strong> değerlerinin her bir <strong>Task</strong> örneği için farklı olması beklenir. Ancak çalışma zamanına baktığımızda aşağıdaki enteresan sonuçlar ile karşılaştığımızı görebiliriz.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg209_Runtime2.gif"><img style="display: inline; border: 0px;" title="blg209_Runtime2" src="/pics/blg209_Runtime2_thumb.gif" alt="blg209_Runtime2" width="286" height="147" border="0" /></a></p>
<p>Görüldüğü üzere bütün <strong>Task</strong> örnekleri <strong>for</strong> döngüsü sayacının son değerini ekrana yazdırmaktadır. Çok kolay bir şekilde gözden kaçabilecek bu vaka nedeniyle uzun süre ekrana baka kalabilir ve arkadaşlarımızın bize “Kal Gelmiş” demelerine neden olabiliriz. Oysa ki çözüm son derece basittir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Threading.Tasks;
namespace Disasters
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
Task.Factory.StartNew((s) =>
{
for (int j = 0; j < 125000000; j++)
{
j++;
j--;
j *= j;
}
int currentI = (int)s;
Console.WriteLine("Güncel Task Id : {0}\tGüncel i : {1}", Task.CurrentId, currentI.ToString());
},i
);
}
Console.WriteLine("Kapatmak için bir tuşa basınız");
Console.ReadLine();
}
}
}</pre>
<p>Bu sefer <strong>StartNew</strong> metodunun farklı bir versiyonu kullanılmıştır. Dikkat edileceği üzere metodun ikinci parametresi olarak<strong> i</strong> değişkeni kullanılmıştır. Bu aslında <strong>State Object </strong>olarak düşünülebilir. Dolayısıyla başlatılan her <strong>Task</strong> örneğine parametre olarak o anki döngü değeri<em>(i değişkeninin değeri)</em> geçirilmektedir. <strong>s</strong> isimli değişken, <strong>for</strong> döngüsünden gelen<strong> i</strong> değişkenini <strong>object</strong> tipinden temsil ettiği için de, basit bir <strong>Cast</strong> işlemi yapılması yeterlidir. İşte çalışma zamanı sonuçları.</p>
<p><a href="https://www.buraksenyurt.com/pics/blg209_Runtime3.gif"><img style="display: inline; border: 0px;" title="blg209_Runtime3" src="/pics/blg209_Runtime3_thumb.gif" alt="blg209_Runtime3" width="283" height="152" border="0" /></a></p>
<p>Görüldüğü gibi Task örnekleri kendilerine atanan i değerlerini ekrana basmıştır.</p>
<p>Elbette farklı vakalar ve felaket senaryoları da söz konusudur. Bu gibi durumları ilerleyen yazılarımızda ele almaya çalışıyor olacağız. Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2010%2f6%2fDisasters.rar">Disasters.rar (22,06 kb)</a><strong> [Örnek Visual Studio 2010 Ultimate Sürümünde Geliştirilmiş ve Test Edilmiştir]</strong></p>2010-06-21T06:08:00+00:00task parallel libraryparallel programmingc# 4.0.net framework 4.0visual studio 2010 ultimatedeadlocklocal variable evaluationexcessive spinningbsenyurtAncak birden fazla iş parçasının da deadlock’ a düşmesi, bir başka deyişle birbirlerini beklemeleri nedeniyle, içinde çalıştıkları Thread’ i(çoğunlukla ana uygulama iş parçası-Main Thread) kitlemeleri söz konusudur. Durumu daha net anlayabilmek için aşağıdaki kod parçasını göz önüne alalım.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=8067c5d1-4313-4e8f-9355-162ca4106f8a0https://www.buraksenyurt.com/trackback.axd?id=8067c5d1-4313-4e8f-9355-162ca4106f8ahttps://www.buraksenyurt.com/post/TPL-Deadlock-Local-For-Variable-Evaluations#commenthttps://www.buraksenyurt.com/syndication.axd?post=8067c5d1-4313-4e8f-9355-162ca4106f8a