async/await ile Asenkron Programlama

22 Oca

Asenkron işlmeler

Asenkron çalışma prensibi, yürütülen süreçlerin uzun sürmesinden dolayı, yürütülmesi gereken diğer süreçlerin beklemeden çalışmasına devam edebilmesi için geliştirilmiştir. Bazen wab ortamındaki bir kaynağa erişip istekte bulunmak ve cevap almak uzun sürebilmektedir. Senkron çalışma prensibinde örneğin bir web kaynağından cevap gelene kadar sıradaki işlem yürütülmez.  Ancak asenkron süreçlerde uzun sürebilecek işlemler farklı bir görev olarak tanımlanarak çalıştırılır ve sıradaki işleme geçilir. Böylece uzun süren işlemler arka planda farklı görevler olarak çalışmaya devam eder.

Asenkron progamlama genellikle Windows Forms, WPF ve Silverlgiht gibi uygulamalarda, uzun süren işlemlerde form arayüzlerinin kullanıcıya cevap verememesi gibi sorunların çözümü için kullanılabilir. Grafik arayüzlü(UI) uygulamalarda tüm grafiksel işlemler tek bir thread üzerinden yürütülür. Örneğin veritabanından yüklüce bir veriyi çekip Datagrid gibi bir nesneye yüklemeye çalıştığımızda thread önce veriyi almalıdır ki aynı thread daha sonra aldığı veriyi Datagrid nesnesine bağlayabilsin. Tabi bu süre içerisinde Formu sürüklemek yada Form üzerindeki diğer bileşenlere ulaşmak da engellenmiştir.

Diğer taraftan dosya işlemleri, resim işlemleri, WCF, soket uygulamaları gibi işlemler de asenkron süreçlerin işletilmesine ihtiyaç duyulabilecek işlemlerdir.

.Net Framework 4.5 öncesinde asenkron API’leri şu sırayla gelişmiştir:

  1. Asynchronous Programming Model (APM)
  2. Event based Asynchronous Pattern(EAP)
  3. Task Parallel Library(TPL) tabanlı, Task-based Asynchronous Pattern(TAP).

Bu yazıda üzerinde durduğumuz konu da TAP modeli ile asenkron programlama seçeneğidir.

TAP modeli ile asenkron programlamanın tam merkezine oturmuş olan “async” ve “await” anahtar kelimelerini inceleyerek devam edelim. Bu iki sihirli kelime, senkron çalışan bir süreci asenkron hale çevirebilmek için kullanılırlar.

Anahtar kelimelerin (async/await) kullanımı

“async” anahtar kelimesi, kullanıldığı metod içerisinde “await” anahtar kelimesinin etkinleştirilmesi  için kullanılır. “async” anahtar kelimesinin kullnaılmadığı metodlarda “await” anahtar kelimesi çalışmaz. Yani “async” anahtar kelimesinin kullanıldığı metodu asenkron hale çeviren bir sihiri yoktur.

Async kullanılan metodlar tıpkı diğer metodlar gibi çalıştırılır. Her hangi bir farkı yoktur. Ta ki derleyici “await” ile karşılaşana kadar işlemler senkron olarak işletilir. İşlemler Thread pool içerisinde bir thread ile yürütülür. “async” ve “await” kelimelerinin kullanımı aşağıdaki gibidir.

private async void download(string url)
{
    var down = await Downloader.DownloadString(url);
}

Yeri gelmişken Thread akışını şu şekilde inceleyebiliriz.

Main metod ve download metodu içerisinde “await” kelimesine kadar olan işlemler Thread 1 ile gerçekleştirilmektedir. “await” kelimesinden sonra işlemler Thread Pool içerisinden başka bir thread olan Thread 3 ile yürütülmüştür.

“await” kelimesinin kullanıldığı yerdeki işlem asenkron şekilde yürütülür. “await” kelimesi matematiksel operatorler gibi düşünülebilir. “await” anahtar kelimesi sadece await edilebilir metodlar ile kullanılabilir. Her metodun önüne “await” klimesini kullanamayız, sadece await için uygun metodlar için bu kelimeyi kullanabiliriz.

Yukarıdaki örnekte System.Threading.Tasks.Task tipine ait Run metodu (awaitable) şeklinde düzenlenmiştir. Artık DownloadStringTaskAsync metodu “await” anahtar kelimesi ile kullanılabilir.

Asenkron metodlar için Unit Test

Yukarıda yazılan örnek metod için bir de unit test yazacak olursak.

Eğer “async/await” kelimelerini unit testler yazarken de kullanmalıyız. Aksi taktirde compailer, Downloader.DownloadString() metodu için bir awaitable Task tipi döndüreceğinden metodumuz testten geçemeyecektir.

Bu yazıda kullanılan örnek kodları barındıran uygulamaya SkyDrive üzerinden bu linkten ulaşabilirsiniz.

Tekrar görüşmek dileğiyle.

Task Parallel Library TLP C#

24 Ara

İşlemci dünyasının gelişimi ve değişimi, işlemcilerin çekirdek sayısı transistör sayısını ne zaman yakalar diye düşündürüyor insana. Bilgisayarlarımızın işlemcileri geliştirkçe programlarımızın daha da hızlandığı aşikardır. Ancak programların hızlanması sadece bilgisayarların fiziksel özellikleriyle alakalı bir durum değildir. Diğer taraftan yazılımların da işlemcileri iyi kullanabilecek şekilde hazırlanmaları gerekmektedir.

İşlemci çekirdekleri arttıkça uzun sürebilecek işlemleri parçalar halinde bölüştürerek işlemcileri tam kapasitede kullanabilmek işimizi daha çok hızlandıracaka ve kolaylaştıracaktır.

Bu yazımızda da paralel işlemlerin alt yapısını oluşturan Task Parallel Library (TLP) konusunu incelemeye çalışacağız.

TPL kavramını .Net Framework 4.0 ile tanımaya başlıyoruz. Bu yapı, işlemlerin paralel bir şekilde yürütülmesini sağlamak amacıyla oluşturulmuştur. TLP’ye ait tipler System.Threading ve System.Threading.Tasks isim alanında bulunmaktadır.

TLP altyapısı aslında süreçlere, görevler olarak bakar ve tıpki insanların görevleri yütebildiği mantıkla organize edilebilmektedir.

  • Yeni görevler oluşturmak, bu görevleri başlatmak, duraklatmak ve sonlandırmak mümkündür.
  • Bir görevin bittiği yerden başka bir görevi başlatmak mümkündür.
  • Başarıyla yerine getirilen görevlerin sonucunda değerler döndürmek mümkündür.
  • Bir görev kendi içinde alt görevler başlatabilir.
  • Görevler aynı veya farklı thread’ler tarafından yerine getirilebilirler.

Task(görev), başarılı bir şeklide tamamlanmasını istediğimiz bir süreçtir. Süreç T1 zamanında başlar ve T2 zamanında sonlanır.

Bu sürecin tamamlanma süresi, işlemcimizin özelliklerine ve kod yazım biçimlerine göre değişebilir.

Thread(ler) ise görevleri yerine getiren işçilerdir. Bu işçiler bir veya birden fazla olabilir. Her görev ayrı bir thread tarafından yürütülmek zorunda değildir. Bir thread birden fazla görevi yerine getirebilir. Ya da bir görev birden fazla thread(multithread) yardımıyla yapılabilmektedir.

Paralel programlama her problemin çözümü için hızlı bir çözüm olmayabilir. Paralel işlemler ile çözüme birden fazla thread yardımıyla yaklaşmış oluyoruz. Örneğin bir apartmanın en üst katına 2 tuğla çıkarmak için bört kişi çağırıp 1 tuğlayı iki kişiye tutturup çıkartmak mantıklı değildir ve maliyetlidir. Bu işi bir kişi gayet seri bir şeklide yapabilir ve uzucdur. Ancak 100 tuğla olduğunda bir kişi  bu işte çok yorulacağından maliyet fazladır. Bu durumda 100 tuğla için dört kişi çağırmak mantıklıdır ve maliyetleri azaltır.

Yazılımsal olarak gelişmiş bir örnek üzerinden giderek sonuca bakacak olursak, paralel ve seri işlemlerin farkını daha rahat görebiliriz.

Bu örneğimizin senaryosu istatistiksel bir formül olan Standart Normal Dağılım’ın matematiksel formülünü döngü yardımıyla farklı sürelerde hesaplatmaya çalışalım. Burada seçilen formülün Standat Normal Dağılım olmasının hiç bir önemi yoktur. İsterseniz x=2y formülünü de kullanabilirsiniz. Ben işlemler uzun sürmesi açısından bu formülü seçtim.

Formülümüz şu şekildedir:

Bu formülü for döngüsünü 10, 100, 1.000, 10.000, 30.000, … şeklinde farklı değerlere kadar seri ve paralel şeklide hesaplattırıp sonucu bir tabloda incelemeye çalışalım.

İşlemi öncelikler seri işlemler mantığı ile yaparak sonuca bakacak olursak, bir işlemin bitmeden diğerinin başlamadığını göreceğiz. Döngümüz işlemi 10000’e kadar 30 kez yapacak.

For döngüsü adımsal işlem gerçekleştirmekte ve  her döngüyü aynı thread gerçekleştirmektedir. Tüm işlemler 1 numaralı thread tarafından gerçekleştirilmektedir.

Şimdi de işlemi paralel olarak gerçekleştirelim. Aynı şeklide işlemi 10000’e kadar 30 kez yapacak.

Paralel işlemlerde kullandığımız tip System.Threading.Tasks.Parallel olacaktır. Paralel for kullanımı şeklideki gibi basitçe ifade edilmektedir ancak arka planda Task tipleri hemen görevleri bölüşürler.

Paralel işlemi incelediğimizde işlemin 4 farklı thread tarafından yapıldığını ve sürenin seri işlemlerden daha uzun sürdüğünü görmekteyiz. Ancak döngü sayısını arttırdığımızda şu şeklide bir tablo karşımıza çıkacaktır.

Döngü sayısı arttıkça paralel işlemlerin adımsal işlemlerden daha kısa sürede gerçekleştiğini görmekteyiz. Örneğimizde kritik sınır 30.000 olarak görünmektedir.

Senaryonun grafiksel ifadesinden de durumu bu şeklide inceleyebiliriz. Bu sistemi test ederken kullandığım laptop biraz eski olduğundan işlemler uzun sürdü. Intel Centrino Core 2 işlemci üzerinde sonuçları bu şeklide aldım. Farklı işlemcilerde sonuçları test edebilirsiniz. Yorumlara yazabilirseniz karşılaştırma yapabiliriz. Ancak benim deneme yaptığım işlemci modelinden kimsede kalmadığından emin gibiyim. Nesli tükendi artık.

Bir sonraki yazıda görüşmek dileğiyle.