Koray Kırdinli

Yazılım ve İş Yaşamı Hakkında Paylaşımlar

WPF Threading Modeli

Yazılım geliştirirken hep şu thread’ler başımın belası olmuştur. Öldürürsün ölmez,  açık kalır, debug ederken sıkıntı, memory şişer bulurken göbeğin çatlar, Pool’a at geri çağır sürekli hep sıkıntı yaratır. Bu dezavantajları olmasına rağmen onsuz da olmuyor ne yazık ki.  Neyseki Microsoft WPF ile birlikte thread kullanımını bana bırak sen business’a odaklan demeye başladı. Çünkü aslında hayal ettiğimiz birinci resimdeki gibi olmasına rağmen, gerçekleşen ne yazık ki ikinci resimdeki gibi olabiliyor :)

HAYALİMDEKİ MULTITHREADING                           ASLINDA OLAN MULTITHREADING
thread1               Thread2
WPF de temel olarak geri planda 2 thread bulunmakta; birisi rendering için diğeri UI’ı yönetmek için kullanılır.
Rendering thread UI’a veri getirirken, ekrana çizim yaparken, eventler’i handle ederken devreye girer.  UI Thread’i ise genellikle 1 tane olur, bazı durumlarda birden fazla da olabilir.

UI Thread’i çalışacak parçaları Dispatcher denen bir nesnede kuyruğa sokar ve öncelik sırasına göre çalıştırır. HEr bir UI threadi en az bir Dispatcher, ve herbir Dispatcher iş parçacıkları sadece 1 thread de çalıştırabilir. WPF de çoğu nesne DispatcherObject sınıfından türer.

Yazdığımız programın müşterileri memnun etmesi için tabi ki müşterinin her butona bastığında, her işlem yaptığında programın hızlıca cevap vermesi gerekiyor ve buradaki anahtar nokta da Dispatcher kuyruğuna atılacak iş parçacıklarının küçük olmasıdır, bu yöntem ile iş parçacıklarını kuyrukta fazla bekletmemiş oluruz.

Genel mantık olarak uzun sürecek işlemleri ana UI thread’inde değil ayrı bir thread de yapmamız gerekiyor. Eskiden windows uygulamaları yazarken bir UI nesnelerine sadece onu oluşturan thread üzerinden erişebilirken, WPF ile birlikte farklı thread’lerden de UI nesnelerini güncelleyebiliyoruz. Örneğin bir datagridi ana thread’den bağımsız olarak arka planda güncelleyebiliyor olmak büyük veriler ile çalışırken çok faydalı olacaktır. WPF bunu ana UI thread’inin dispatcherına iş parçacıkları register ederek sağlıyor. Dispatcher.Invoke, Dispatcher.BeginInvoke metodları bu register işlemini yapar. Invoke senkron, BeginInvoke asenkron çalışır. Bunların önceliklerini de DispatcherPriority enumu ile belirtebilirsiniz.

Örnek kullanım : Yeni bir thread açarak dispatcher invoke yöntemiyle UI daki listbox ekran kilitlenmeden doldurulabiliyor.

Thread tt = new Thread(FillListBox);
tt.Start();

private void FillListBox()
{
for (int i = 0; i < 500; i++)
{
listBox1.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate()
{
listBox1.Items.Add(“A” + i.ToString());
});

Thread.Sleep(100);

}
}

Reklamlar

Mayıs 28, 2015 Posted by | C#, WPF | , , , , | Yorum bırakın

C# da object initializer kullanımı

Object initializer C# 3.0 ile birlikte hayatımıza girdi. Object initializer kullanmak hem daha okunaklı hem de daha güvenli bir kod yazma yöntemi sunuyor.

Aşağıda object initializer ve normal yollarla yaratılan 2 nesne örneği bulunmakta. Klasik yöntem kullanırsak multithread uygulamalarda nesnenin parçalı olarak yaratılma ihtimali var. Bunu engellemek için ayrıca lock koymak gerekir. Ancak object initializer ile nesneler yaratıldığında bu risk ortadan kalkar.

Bir nesnenin object initializer ile yaratılması :

StudentName student = new StudentName
{
FirstName = "Koray",
LastName = "Kırdinli",
ID = 116
};

Nesnenin klasik yöntem ile yaratılması.

StudentName _tempStudent = new StudentName();
_tempStudent.FirstName = "Koray";
_tempStudent.LastName = "Kırdinli";
_tempStudent.ID = 488;
StudentName student = _tempStudent;

Aşağıdaki linkte olayı kavramak için güzel bir örnekten faydalanılmış.
http://community.bartdesmet.net/blogs/bart/archive/2007/11/22/c-3-0-object-initializers-revisited.aspx

Şubat 18, 2014 Posted by | C# | , , | Yorum bırakın

Threading

Bir uygulama harddiskte çalışmadan durduğu zaman buna program denir, program çalışmaya başladığı zaman rame yüklendiği zaman ise process diye adlandırılır.Bu process’leri ise işleyen bilgisayar arabirimi işlemcidir. Herhangi bir t anında aynı anda birden çok process’in işlenmesine ise multi-tasking adı verilir. İşlemci process’leri küçük threadler halinde sanki anyı anda çalışıyormuş gibi sıraya sokar ve kullanıcya hisettirmeden sırayla çalıştırır.
Threading çoklu işlemleri eşzamanlı olarak işlemeye yarar. Eğer bir uygulamayı tek kanallı olarak programlarsak çok işlemli bir bilgisayarda diğer işlemcileri boşa harcamış oluruz.
.NET’te Threadler ile çalışmaya başlamak için öncelikle Thread sınıfını incelemenin faydalı olacağı kanaatindeyim. Thread aslında bağımsız olarak bir kanalı kullanan küçük kod parçasıdır diyebiliriz ve bu kod parçalarını ; her bir kanalı başlatabilir (Start), durdurabilir(Abort) veya yaşıyor mu(IsAlive) kontrol edebiliriz.
Threadler ile çalışabilmek için
System.Threading namespace’ini using kısmına eklememiz gerekmektedir.

Bir Threadi başlatabilmek için öncelikle ThreadStart delegesi oluşturulur. Bu delege void dönen bir metod parametre alır ve o metodun başlangıç adresini temsil eder.Daha sonra Thread’e parametre olarak ThreadStart delegesi verilir ve Thread başlatılır.

void SimpleWork()

{

MessageBox.Show(String.Format(“Thread {0}”, Thread.CurrentThread.ManagedThreadId));

}

ThreadStart ts = newThreadStart(SimpleWork);

for (int i = 0; i < 5; i++)

{

    Thread t = newThread(ts);

    t.Start();

    Thread.Sleep(1000);

}

Eğer bu uygulamayı thread ile yapmasaydık ManagedThreadId hep aynı olacaktı ancak her seferinde yeni bir thread oluşturduğumuz için farklı id ler elde ediyoruz.

Thread.Abort(): Theadin derhal sonlandırılmasını sağlar.Bu metodu çağırdığımızda ThreadAbortException hatası fırlatılır. Eğer Abort işlemi doğru yerde yakalanırsa bu hata Framework tarafından yakalanır. Bu hataya düşmemek için önemli kodu Thread.BeginCriticalRegion ve Thread.EndCriticalRegion arasına yazmak gerekebilir.

Thread.Join Metodu : Pratikte bir threadi sonlandırmak için kendi içerisinde yaratılan başka bir threadin beklenmesi gerekebilir.Bu gibi durumlarda Join metodu kullanılır.

ThreadStart ts = newThreadStart(SimpleWork);

Thread[] threads = newThread[5];

for (int i = 0; i < 5; ++i)

{

    threads[i] = newThread(ts);

    threads[i].Start();

}

 

foreach (Thread item in threads)

{

    item.Join();

}

Threadlerin öncelikleri ThreadPriority enumarasyonu ile belirleyebiliriz.
Highest,AboveNormal,Normal,BelowNormal,Lowest.

Yukarda anlattığımız gibi ThreadStart delegesi sadece void dönen ve parametre almaayan metodları temsil edebilir.Gerçek hayatta ise parametre alan metodları da temsil etme ihtiyacı duyabiliriz.Bunun için ThreadStart delegesi yerine ParameterizedThreadStart delegesi kullanılır.ParameterizedThreadStart delegesi void dönen ve parametre olarak object alan metodları temsil edebilir.

void WorkWithParameterMetod(object o)

{

    string info = (string)o;

    for (int i = 0; i < 10; ++i)

    {

        Console.WriteLine(“{0} {1}”,info,Thread.CurrentThread.ManagedThreadId);

        Thread.Sleep(10);

    }

}

ParameterizedThreadStart pts = newParameterizedThreadStart(WorkWithParameterMetod);

Thread t = newThread(pts);

t.Priority = ThreadPriority.Highest;

t.Start(“KORAY”);

Thread t2 = newThread(pts);

t2.Start(“KIRDİNLİ”);

Multithreading programlama yaparken programcıyı en fazla zorlayan şeylerin başında farklı threadler arasındaki veri alış verişi gelmektedir. Threadler arasında paylaşılan veriyi güvenli bir şekilde threadler arasında transfer edebilmek önemlidir neyse ki .NET Framework bu işi kolaylaştırıyor.
Aşağıdaki örnekte Counterın 10000 olmasını beklerken threadSayisi*10000 olduğunu göreceğiz.

publicclassCounter

{

    publicstaticint Count;

}
publicvoid UpdateCount()

{

    for (int i = 0; i < 10000; i++)

    {

        Counter.Count += 1;

    }

}

ThreadStart ts = newThreadStart(UpdateCount);

Thread[] threads = newThread[10];

for (int i = 0; i < 10; i++)

{

    threads[i] = newThread(ts);

    threads[i].Start();

}

//Threadlerin bitmesini bekle

for (int i = 0; i < 10; i++)

{

    threads[i].Join();

}

 

Console.WriteLine(“Toplam:{0}”,Counter.Count);
//Eğer bu kodu çok işlemli bir bilgisayarda çalıştırırsanız Sonuç 10000 değil 10000*10=100000; olacaktır.
Bu durumu engellemenin yolu Interlocked sınıfını kullanmaktır.UpdateCount metodunda for döngüsü içine Counter.Count += 1; yerine aşağıdaki kod eklenir.
Interlocked.Increment(refCounter.Count);

 

UpdateCount metodunu senkronize etmek için lock(this){//—} arasına kodları yazılır. Senkronizasyon lock kullandığımızda UpdateCount metodunun koduna aynı aynı anda sadece bir threadin erişmesini faranti ederiz.

Monitor.Enter(this); static metodu belirtilen nesneyi kilitler.
Monitor.Exit(this); Kilidi kaldırır. Bunları kullanırsak lock metoduna gerek kalmaz.

Deadlock : Eğer iki kod parçası aynı nesneye aynı anda erişmeye çalışırsa deadlock oluşur.Deadlocklardan kurtulmak için Monitor.TryEnter metodu kullanılabilir ve lock ların sayısı azaltılabilir.

Diğer senkronizasyon metodları : ReaderWriterLock ,

Windows Kernel Senkronizasyon Nesneleri : Mutex(lock gibidir) ,Semaphore, AutoResetEvent, ManualResetEvent : Bu nesneler senkronizasyonu sağlamak için kullanılabilir ancak örneğin Monitor yerine Mutex kullanmak 33 kat daha yavaştır.

Asenkron Programlama Modeli (APM) : Bu model basitçe farklı kod parçalarını farklı threadlerde çalıştırma olarak tanımlanabilir.Bu sistem kaynaklarını daha efektif kullanabilmemizi sağlar.Nesnelerin BeginXXX ve EndXXX patternine uygun metodları varsa asenkron kod çalıştırılabilir demektir.

byte[] buffer = newbyte[100];

string filename = string.Concat(Environment.SystemDirectory, “\IE7Eula.rtf”);

FileStream fs = newFileStream(filename, FileMode.Open, FileAccess.Read,

    FileShare.Read,1024, FileOptions.Asynchronous);

 

IAsyncResult result = fs.BeginRead(buffer, 0, buffer.Length, null, null);

int numBytes = fs.EndRead(result);

fs.Close();

Console.WriteLine(“Read {0} bytes”,numBytes);

   Console.WriteLine(BitConverter.ToString(buffer));

 

Bundan önceki örneklerimizde hep kendi Threadlerimizi yaratıp bu threadler üzerinden işlem yaptırdık fakat kendi threadini yaratmak tavsiye edilen bir yöntem değil.Bunun yerine .NET’in kendi ThreadPool’unu kullanmak daha sağlıklı olacaktır.

//Bu kod yeni bir thread oluşturmaz ancak havuzda var olan bir threadi tekrardan

//kullandığı için daha hızlıdır ve daha az memory harcar.

WaitCallback workItem = newWaitCallback(WorkWithParameterMetod);

if (!ThreadPool.QueueUserWorkItem(workItem, “ThreadPooled”))

{

    Console.WriteLine(“Could not queue item”);

}

int threads,completionPorts;

ThreadPool.GetMaxThreads(out threads, out completionPorts);

ThreadPool.SetMaxThreads(threads + 10, completionPorts + 100);

 

ASP.NET uygulamalarında Windows uygulamalarından farklı olarak SynchronizationContext sınıfı ile işlemler yapılır.

SynchronizationContext ctx = newSynchronizationContext();

ctx.Send(RunMe,“Hi”);

ctx.Post(RunMe,“Hi”);

Threading hakkında anlatacaklarım bu kadar umarım yararlı olmuştur.

Mayıs 25, 2010 Posted by | C# | , , , , | Yorum bırakın