Lock, Monitor, Mutex ve Semaphore
İşletim sistemleri prosesler arasında ya da proses içindeki thread'ler arasında senkronizasyonu sağlamak için birtakım mekanizmalar sunmaktadır. Bu yazıda C# ile bu senkronizasyon mekanizmalarını nasıl kullanabileceğimizi göreceğiz. Tabii ki benzer amaca yönelik bu kadar çeşitli mekanizmanın arasındaki benzerlik ve farklılıkları da anlamaya çalışacağız.
Lock
private static readonly object kilit = new object(); private int sayac; public void IslemYap() { lock (kilit) { sayac++; if (sayac == 10) BaskaBirIslemYap(); } }
lock deyimi C# dilinin sunduğu pratik bir senkronizasyon mekanizmasıdır. IslemYap() metodunun birden çok thread tarafından işletilmekte olduğunu düşünelim. lock (kilit) aşamasına ilk gelen thread ilgili bloğun işletim hakkını kazanır. Bu blok bu thread tarafından tamamlanmadan başka bir thread aynı aşamaya geldiğinde ilk thread'in işini bitirmesini beklemek zorundadır. Bu thread'lerin sayısı ne kadar çok olursa olsun hepsi beklemek zorundadır. İlk thread bloğun işletimini bitirdiği anda bloğun işletim hakkı bekleyen thread'ler arasında ilk-gelen-ilk-hizmet-alır mantığıyla diğer bir thread'e geçer. Böylelikle bu örnekte sayac değişkeninin değerinin artması ve ardından değerinin test edilmesi işlemi bölünmeden gerçekleşmiş olur.
Monitor
C# dilindeki lock deyimi Monitor.Enter ve Monitor.Exit çağrılarının kısayolundan başka birşey değildir. Önceki örneği şu şekilde de yazabilirdik:
private static readonly object kilit = new object(); private int sayac; public void IslemYap() { Monitor.Enter(kilit); try { sayac++; if (sayac == 10) BaskaBirIslemYap(); } finally { Monitor.Exit(kilit); } }
Monitor sınıfı aynı zamanda TryEnter isminde milisaniye ya da TimeSpan cinsinden timeout süresi alan bir metod içeriyor. Bu metod verilen timeout süresi içerisinde ilgili kilitleme nesnesi üzerinde kilit elde edemezse beklemeksizin false döndürüyor. Ayrıca, TryEnter metodunun timeout süresi olmayan bir şekli de hemen o anda kilitleme nesnesi üzerine kilit konulamazsa false dönüyor yani bir nevi kilit durumunu test etmeye yarıyor.
Mutex
Lock ve Monitor aynı proses içerisindeki thread'lerin senkronizasyonu için kullanılırken Mutex sistemdeki prosesler arasında senkronizasyon sağlamak için kullanılmaktadır. Mutex'te kilitleme ve kilit kaldırma için WaitOne ve ReleaseMutex metodları kullanılmaktadır.
Mutex'in kullanımı için genelde aynı uygulamanın aynı bilgisayarda aynı anda birden çok açılmasının engellenmesi örneği verilmektedir. Biz burada birden çok uygulamanın aynı günlük (log) dosyasına satırlar eklemesinin senkronizasyonunu göreceğiz. Diyelim ki şöyle bir kodumuz var ve bunu Mutex kullanarak uygulamalarımız arasında da güvenli kılmak istiyoruz (yani log dosyasını aynı anda birden çok uygulama yazma hakkıyla açmak istediği için sorun yaşıyoruz ve bir şekilde bunları düzene sokmak istiyoruz) :
public static void LogYaz(String s) { File.AppendAllText("YPLog.log", s); }
Bu kodu şu şekilde güvenli hale getirebiliriz:
private static Mutex mtx = new Mutex(false, "YazilimProjesi_LogMutexi"); public static void LogYaz(String s) { mtx.WaitOne(); try { File.AppendAllText("YPLog.log", s); } finally { mtx.ReleaseMutex(); } }
Lock deyiminde olduğu gibi, bir mutex sadece onu kilitleyen proses tarafından serbest bırakılabilir. Mutex'le ilgili ek bir bilgi daha verelim; ReleaseMutex metodu çağrılmadan uygulama sonlanacak olursa, CLR tarafından ilgili mutex otomatik olarak serbest bırakılır.
Semaphore
Semaphore, lock ve mutex'in aksine senkronize erişimi tamamen bekletmek değil sınırlandırmak için kullanılır. Bu ne demek? Diyelim ki sınırlı bir kaynağımız var, aynı anda 5 thread'in erişimi sistem için sorun olmuyor ama 5'ten fazla thread tarafından o kaynak kullanılmak istendiğinde performans sorunu yaşanıyor ve dolayısıyla kodun o bölümünü bütün thread'lerin erişimine açmak istemiyoruz. Bu durumda sayaçlı bir lock mekanizmasına ihtiyacımız var. İşte o mekanizmanın adı Semaphore.
// İzin verilen = 5, başlarken kullanılmış = 0 private Semaphore sem = new Semaphore(0, 5, "YazilimProjesi_Semaphore"); public void IslemYap() { sem.WaitOne(); try { YogunKaynakKullananIslem(); } finally { sem.Release(1); // Sadece bir kilit kaldırılsın } }
Şunu da belirtelim ki lock ve mutex'i sadece kilitleyen thread/proses kilidi kaldırabiliyorken semaphore üzerindeki kilidi herhangi bir thread/proses kaldırabilir. Ayrıca, semaphore da mutex gibi isim verilerek yaratıldığında prosesler arası senkronizasyon için de kullanılabilmektedir.
- 20608 okunma
Yorumlar
4 yorumTeşekkürler bilgilendirici bir makale olmuş..
Teşekkürler elinize sağlık.Lock ve Monitor de kullandığınız kilit nesnesi ne işe yarıyor acaba?
Bu kilit nesnesi, senkronize etmek istediğimiz thread'ler tarafından erişilebilir bir kapsam içerisinde olmalıdır. Yani, "kilit" isimli nesneyi IslemYap() metodunun içinde lokal değişken olarak tanımlasak anlamsız olurdu ve işe yaramazdı. Monitor ya da lock için bir kilit nesnesi kullanabiliyor olmak, aynı anda birden çok amaca yönelik olarak farklı ve birbirinden bağımsız kilitleme bölgeleri oluşturabilmeyi sağlıyor. Makaledeki örnekler olayı basitçe kurgulamak üzere verildiği için orada gereksizmiş gibi görünebilir. Ama gerçek bir uygulamada paralel işleyen ve farklı görevleri olan thread'leri senkronize etmekte ve kritik bölgeleri korumada farklı kilit nesneleri gerekir. Daha açık nasıl anlatılır bilmiyorum :)
Kısa ve öz anlatım için teşekkürler.
Yeni yorum gönder