Threadlerin Doğru Yönetilmesi Ve Race Condition

Selamlar,

Bugün bir projede karşılaştığım race condition durumunu, sizler ile bu makalede tartışmak istiyorum. Aynı anda çalışan farklı Taskların birbirlerine bağımlı yani, çalışma zamanlarının birbirleri ile kesişmeden yürütülmesi gerekiyordu. Biz bu soruna Race Condition diyoruz.

Paylaşılan memoryi kullanan processlerin, kendisine ait işleri yanlış zamanda yapıp, verilerin bozulduğu duruma race condition denir. Esas amaç processlerin doğru sırada çalışmasının sağlanmasıdır. Mesela hangi process’in önce çalıştırılacağı, oradaki değeri hangisinin alacağı ve nasıl değiştireceği race condition’ı ilgilendiren en güzel örneklerdir. Race, kelimesinden de anlaşılacağı gibi, processlerin birbirleri ile yarışmasıdır. Hangi processin hangi zamanda çalıştıracağı tamamen bizim sorumluluğumuzdadır.

Öncelikle gelin problem çıkabilecek senaryomuzu oluşturalım: 3 farklı methodumuz var.

  1. Ekrana 5 tane “*” karakteri basacak.
  2. Ekrana 5 tane “+” karakteri basacak.
  3. Ekrana 5 tane “$” karakteri basacak.

Aşağıdaki kodun ekran çıktısı sürekli farklılık gösterir. Örnek 4 ekran çıktısı aşağıdaki gibidir.

result

Halbuki amacımız ekrana birbirlerine karışmadan 5 tane  “*” , “+” ve  “$” yazdırmaktır. Bu durumda tasklar birbirlerini beklemek zorundadırlar. İşte bugün bu sorunu çözebilecek 4 farklı yöntemi inceleyeceğiz.

task

Image Source: https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/Thread_pool.svg/580px-Thread_pool.svg.png

1-) Lock İle Synchronized Etmek: Aşağıda görüldüğü gibi ortak kullanılan static bir “lock” object ile ilgili taskler birbirlerini beklemektedir. Bu açıdan bakıldığında ilgili taskların parallel çalışılması engellenmiş ama bağımlı işlemlerin birbirini ezmesi durumunun önüne geçilmiştir.

Örnek Ekran Çıktısı: 

untitled

Aynı işlemleri “Task“‘ler yerine “Thread” ile yapmak istese idik aşağıdaki gibi bir çözüme gitmemiz gerekecekti:

Not: Yukarıdaki örnekde aklınıza takılan bir durum olabilir :) Hangi Task’in önce başlayacağı belirlenememektedir. Zaten Race durumu, adını da tam buradan almıştır. Hangi taskin başlıyacağı, her çalıştırma işleminde değişmektedir. İşte aşağıdaki “Join()” methodu ile istenen Thread’in istenen sırada başlatılması ve birbirini beklemesi sağlanmıştır.

2-) Thread.Join() İle Synchronized Etmek : 

3-) Task.ContinueWith İle Synchronized Etmek : Peki “Thread” değil de istenen bir “Task“‘den başlanılması ve senkron bir şekilde çalışması istense idi yapılması gerekenler ne idi?  İşte bu noktada “ContinueWith()” methodu devreye girmektedir. Başlatılan thread’in bitmesi durumunda, method içine yazılan yeni thread çalıştırılır.

4-) Monitor Enter İle Synchronized Etmek :  Son olarak aşağıdaki örnekde “Monitor.Enter() ve Monitor.Exit()” methodu ile thread’lerin birbirlerini beklemesi sağlanmıştır. “Lock statement“‘ın aynısıdır. Aşağıda farklı olan durum, tüm “Thread”‘lerin bir List’e eklenmesi ve daha sonra tümünün tamamlanmasının “Any()” linq methodu ile beklenilmesidir.

Böylece Multithreading bir projede, threadlerin istenen sırada ve birbirlerini ezmeden nasıl çalıştırılabileceklerini hep beraber inceledik.

Bir maklenin daha sonuna geldik.

Yeni bir makalede görüşmek üzere hoşçakalın.

Kaynaklar: https://www.csharpstar.com/csharp-race-conditions-in-threading/

Herkes Görsün:

Bunlar da hoşunuza gidebilir...

6 Cevaplar

  1. Ali Eren Sevinç dedi ki:

    Faydalı ve anlaşılır bir makale olmuş teşekkürler.

  2. Can dedi ki:

    Çok güzel ve faydalı bir yazı.

  3. Cengiz dedi ki:

    Teşekkürler Bora bey , ellerinize sağlık

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.