Koray Kırdinli

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

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 25, 2010 - Posted by | C# | , , , ,

No comments yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s