Projelerimiz içerisinde tamamlanması uzun süren işlemlerin, çalışmakta olan uygulamayı bir süreliğine kitlediğine sıkça rastlamaktayız. Çünkü metotlar thread içerisinde çalışırlar ve bir thread içerisinde sadece tek bir metot çalışabilir. İlgili metot işini bitirdiğinde sıradaki metot çalışır. Dolayısı ile zaman alan bir işlem olduğunda uygulamalarımızda geçici duraksamalar yaşayabiliriz.
Aynı thread içerisinde tek bir metodun çalışabildiğini görmek adına ufak bir deneme yapalım.
class Program
{
static void Main(string[] args)
{
int sonuc = Topla(5, 10);
Console.WriteLine("Main kaldığı yerden devam ediyor...");
}
static int Topla(int a, int b)
{
Thread.Sleep(5000);
return a + b;
}
}
Program Main metodu ile başlayacaktır. İlk satırda çağırdığımız Topla metodunun işlem süresini sembolik olarak uzatmak için Thread sınıfının Sleep metodunu kullandık. Bu sayede metot içerisinde 5 saniyelik bir duraksama olmasını sağlamış olduk.
Programı çalıştırdığımızda 5 saniyelik bir beklemenin ardından “Main kaldığı yerden devam ediyor…” yazısını göreceğiz. Çünkü main metodu içerisinde çağırılan Topla metodu çalışmaya başladığında, Main metodu durur. Topla metodu işini bitirdikten sonra ise Main metodu kaldığı yerden devam eder.
Bu son derece basit bir örnekti. Ancak uygulamalarımızda can sıkıcı durumlarla karşılaşmamız muhtemeldir. Bu sebeple bazı durumlarda asenkron çağrılar yapmayı tercih ederiz. Bu çağrılar delegeler aracılığı ile gerçekleştirilebilir. Delegeler metot adresi tutan 5 temel tipten biridir. İlgili metodu bir delege ile işaret ederek farklı bir thread içerisinde çalıştırmak mümkündür. Bu yazımızda asenkron çağrı yapmak için kullanılabilen 2 farklı tekniği ele alacağız.
1) Polling Tekniği
Bir delege ile şaret edilen metodun, işini bitirip bitirmediğinin her adımda kontrol edildiği bir yöntemdir. Girişte verdiğimiz basit örnek üzerinden devam edelim, yukarıda senkron olarak yapılan çağrıyı asenkron hale getirelim. Bunun için işlem süresi uzun süren metodumuzu bir delege ile işaret edelim ve bu delege üzerinden “BeginInvoke” metodu ile asenkron çağrıyı başlatalım.
class Program
{
static void Main(string[] args)
{
Func<int, int, int> t = Topla; //metodumuzu, imzası ile uygun bir delege ile işaret ediyoruz.
IAsyncResult asy = t.BeginInvoke(10, 20, null, null); //asenkron çağrıyı başlatıyoruz.
bool islemBitmistir = false; //Topla metodunun işinin bitip bitmediğini belirten bir değişken tanımlıyoruz.
for (int i = 0; i < 100; i++)
{
if (asy.IsCompleted && !islemBitmistir)
{
Console.WriteLine("SONUÇ : {0}", t.EndInvoke(asy).ToString());
islemBitmistir = true;
}
Thread.Sleep(500);
Console.WriteLine(i);
}
}
static int Topla(int a, int b)
{
Thread.Sleep(5000);
return a + b;
}
}
IsComplated property’si işlemin bitip bitmediğini göstermektedir. İkinci bir parametre tanımlamamızın sebebi, işlem bittiğinde, if bloğunun içerisine girilmesini önlemektir.
if (asy.IsCompleted)
{
Console.WriteLine("SONUÇ : {0}", t.EndInvoke(asy).ToString());
islemBitmistir = true;
}
Eğer kodumuzu yukarıdaki gibi yazsaydık, işlem bittikten sonra her adımda ekrana sonucu yazdıracaktı. Ancak biz sadece işlemin bittiği anda sonucu alıp, sonraki adımlarda bu sonucu ekranda görmek istemediğimiz için ikinci bir değişken kullanarak if bloğu içerisine girişi önlemiş olduk.
BeginInvoke metodunun parametrelerini incelediğimizde, ilk iki parametrenin Topla metoduna giden parametreler olduğunu görmek mümkün. üçüncü parametre AsyncCallback tipinde bir delegedir ve parametre olarak IAsyncResult alarak geriye void döndürebilen metotları işaret eder. Bu parametrenin ne olduğunu Callback tekniği içerisinde yapacağız. Son parametre ise object tipindendir ve asenkron çağrı sırasında ihtiyacımız olan herhangi bir değer varsa gönderebilmemizi sağlar.
2) Callback Tekniği
Polling tekniğinde olduğu gibi her adımda kontrol yapmak yerine, kontrolün işlem bitiminde tek seferde yapıldığı tekniktir. Sembolik olarak 5 saniye süren KareAl adlı bir metodu asenkron olarak çağıralım.
class Program
{
static void Main(string[] args)
{
Func<int, int> t = KareAl;
t.BeginInvoke(55, KareAlCallback, t);
for (int i = 0; i < 100; i++)
{
Thread.Sleep(500);
Console.WriteLine(i);
}
}
static void KareAlCallback(IAsyncResult res)
{
Func<int, int> t = (Func<int, int>)res.AsyncState; ;
int sonuc = t.EndInvoke(res);
Console.WriteLine("SONUÇ : {0}", sonuc.ToString());
}
static int KareAl(int sayi)
{
Thread.Sleep(5000);
return sayi * sayi;
}
}
BeginInvoke metodundaki ikinci parametre bir delegedir, bir metot parametre olarak bir delege alıyorsa, bir metot bekliyor demektir. Bu metot IAsyncResult tipinden parametre alan ve geriye void dönen bir imzaya sahip olmalıdır. KareAlCallback adlı metodumuzu bunun için yazdık. İşlem bittiğinde bu metot çalışır ve IAsyncResult tipindeki değişken ile sonucu alabiliriz.
KareAlCallBack adlı metot sadece BeginInvoke metoduna parametre olarak verildiğinden ötürü, bu metodu anonim olarak tasarlayabiliriz. Yani kullan-at tarzı bir metot yazmamız daha doğru olacaktır.
class Program
{
static void Main(string[] args)
{
Func<int, int> t = KareAl;
t.BeginInvoke(55, res => Console.WriteLine("Sonuç : {0}", t.EndInvoke(res)), null);
for (int i = 0; i < 100; i++)
{
Thread.Sleep(500);
Console.WriteLine(i);
}
}
static int KareAl(int sayi)
{
Thread.Sleep(5000);
return sayi * sayi;
}
}
Programı çalıştırdığımızda, Main içerisindeki for döngüsü devam ederken, başka bir thread içerisinde de KareAl metodu çalışmaktadır. Dolayısı ile Main metodu çalışırken, KareAl metodunun sonucunu görebilmemiz mümkündür.
Bu makalemizde Asenkron çağrı yapabileceğimiz 2 tekniği incelemeye çalıştık. Faydalı olması dileği ile…
4 yorum:
çok teşekkürler
Paylaşım çok güzel olmuş :)
Gerçekten önemli bir konuyu sade ve en anlaşılır şekilde anlatmışsınız. Hatta nokta atışı diyebilirim. Emeğinizie sağlık.
tşk ederim. çok sade ve anlaşılır bir makale olmuş.
Yorum Gönder