C#‘da Singleton Üyelerine Thread-Safe
Erişim Sağlamak
Birçok C# uygulamasında, bir sınıfın yalnızca bir örneği olduğundan emin olmak ve o örneğe küresel bir erişim noktası sağlamak için singleton deseni yaygın olarak uygulanır. Ancak, birden fazla thread bir singleton’ın üyelerine eriştiğinde, thread güvenliği ile ilgili endişeler doğar. Bu blog yazısı, bu soruna derinlemesine bakacak ve özellikle bir singleton sınıfının Toggle()
yöntemine odaklanarak, onun içindeki thread-safe işlemleri nasıl sağlayacağımızı ele alacaktır.
Problemi Anlamak
Aşağıdaki gibi bir singleton sınıfını C#‘da düşünelim:
public class MyClass
{
private static readonly MyClass instance = new MyClass();
public static MyClass Instance
{
get { return instance; }
}
private int value = 0;
public int Toggle()
{
if(value == 0)
{
value = 1;
}
else if(value == 1)
{
value = 0;
}
return value;
}
}
Buradaki Toggle()
yöntemi, value
değerini 0 ile 1 arasında değiştirmek için tasarlanmıştır. Ancak, birden fazla thread aynı anda Toggle()
çağrısı yaparsa, yöntem thread-safe değildir. Sonuç olarak, öngörülemeyen davranışlar ve yanlış sonuçlar oluşabilir.
Neden Toggle()
Thread-Safe Değil?
İki thread aynı anda Toggle()
eriştiğinde, yürütmeleri, her iki thread’in de aynı value
değişkenini okuması ve değiştirmesi açısından üst üste binebilir. Bu durum, beklenen sonucun gerçekleşmediği senaryolar oluşturabilir. Örneğin, her iki thread de aynı anda value
’yu değerlendirirse bir yarış durumu meydana gelebilir.
Yarış Durumu Örneği
// Thread 1'in yürütmesi
if(value == 0)
{
value = 1;
// Şimdi Thread 2 devreye giriyor
// Thread 2 value'yu 1 olarak değerlendiriyor ve 0 yapıyor
}
// Thread 1 0 döndürüyor, bu da beklenen bir değer değildi.
Toggle()
‘u Nasıl Thread-Safe Hale Getiririz
Kilitleme Prensibi
Thread güvenliğini sağlamak için, bir thread kritik bir kod bölümünü (bu durumda, value
‘yi değiştirmek) yürütürken başka bir thread’in müdahale edemeyeceğinden emin olmamız gerekir. C#’da bunu lock
ifadesini kullanarak uygulayabiliriz.
Adım Adım Çözüm
-
Kritik Bölümleri Belirleme: Hangi kod bölümlerinin kesintisiz yürütülmesi gerektiğini belirleyin. Bu durumda
value
‘nin okunması ve yazılması. -
Kilitleme Nesnesi Oluşturma:
value
bir değer tipi (birint
) olduğu için kilitlenemez. Bu nedenle, kilitleme için ayrı bir nesneye ihtiyacımız var.
private static readonly object locker = new object();
- Kodun Kilit İçine Alınması:
value
‘yi değiştirmek için bir kritik bölüm oluşturmak üzerelock
ifasını kullanın.
Revize Edilmiş Toggle Yöntemi
Thread güvenliğini sağladıktan sonra Toggle()
yönteminin nasıl görüneceği:
public int Toggle()
{
lock (locker)
{
if(value == 0)
{
value = 1;
}
else if(value == 1)
{
value = 0;
}
return value;
}
}
Ana Noktalar
- Kilitleme Mekanizması: Belirlenen bir nesne üzerinde kilitleme yaparak, yalnızca bir thread’in bir zamanda kritik bölüme erişmesini sağlarsınız.
- Değer Tiplerini Kilitlemekten Kaçının: Sadece referans tipleri üzerinde kilitlenebileceğinizi unutmayın; bu nedenle bir
locker
nesnesine ihtiyaç vardır. - Uygulama Tamamlığı: Yarış durumlarını önlemek için, eşzamanlı erişimden etkilenebilecek kod bölümlerini her zaman gözden geçirin ve belirleyin.
Sonuç
Singleton sınıfınızı thread-safe hale getirmek, çok iş parçacıklı bir ortamda beklenmedik davranışların önlenmesi için çok önemlidir. Kilitler kullanarak, paylaşılan kaynakları etkili bir şekilde koruyabilirsiniz. Singleton deseni, thread güvenliği düzenlemeleri yapıldığında uygulama stabilitesini ve doğruluğunu sağlamakta güçlü bir araç olabilir.
Sonuç olarak, singleton’larda thread güvenliğini anlamak, karmaşık uygulamalar üzerinde çalışan her C# geliştiricisi için önemli bir beceri haline gelmektedir.