Semaphore, Mutex ve Spinlock nedir ve ne işe yarar?

Bu flood’da Mutex, Semaphore ve Spinlock Semaphore’lardan bahsedeceğim.

İşletim sisteminin eş zamanlı çalıştırdığı iş parçacıkları proses olarak adlandırılır. Prosesler de kendi içlerinde eş zamanlı çalışan iş parçacıklarına sahip olabilir ve bunlara da Thread denir.

Farklı prosesler arasında ya da aynı proses içinde yer alan Thread’ler arasında zaman zaman senkronizasyon ihtiyacı duyulur çünkü bu Thread’ler görevlerini yerine getirebilmek için işletim sistemi tarafından sağlanan veya prosesin kendisinin tuttuğu paylaşılan bir kaynağa erişmek isterler. Örnek olarak Thread’lerin bir log dosyasına veya ekrana log yazmak istedikleri durumu ele alalım. İki Thread aynı anda log dosyasına yazmaya çalışırsa dosyaya yazılan loglar birbirine karışacak ve okunmaz hale gelecektir. Burada Thread’leri log dosyasına yazma konusunda sıraya sokacak bir mekanizmaya ihtiyaç vardır.

Mutex (MUTual EXclusion)’ler tam da bu mekanizmayı sağlamak için tasarlanmıştır. Mutex’ler uygulamanın yazıldığı dil ve Runtime tarafından sağlanan basit veri yapılarıdır. Farklı Thread’ler tarafından paylaşılan her bir kaynak için kaynağa olan erişimi düzenlemek üzere bir Mutex yaratılır. Paylaşılan kaynağa erişim yapılan kod bölgesi Critical Section olarak adlandırılır. Kaynakla işi olan Thread, Mutex'in sahipliğini almaya (Acquire) çalışır. Mutex o anda başka bir Thread tarafından tutulmuyorsa Thread Mutex'i alır, Critical Section'a girerek ilgili kaynağı kullanır. Diğer durumda, yani Mutex o anda başka bir Thread tarafından kullanılıyor ise, ikinci Thread işlemci tarafından beklemeye alınır. Mutex'i tutan Thread Critical Section'ı bitirip Mutex'i bırakırken, halihazırda Mutex'in bırakılmasını bekleyen Thread uyandırılır ve Mutex'in sahipliğini alarak Critical Section'a girer ve paylaşılan kaynağa erişim sağlar.

Semaphore’lar bazı platformlarda Binary ve Counting olarak ikiye ayrılsa da temel olarak bir ya da daha fazla sayıda bulunan kıt kaynağın birçok kullanıcı tarafından sırayla kullanılmasını sağlarlar.

Basit bir örnek olarak 4 araç kapasiteli bir otopark otomasyon sistemi kullanılabilir. Bu sistemde giriş ve çıkış kapısını kontrol eden iki Thread’li uygulamada Semaphore Count’u 4 verilmiş bir Counting Semaphore kullanılmış olsun. Otopark boşken, giriş kapısını yöneten Thread Semaphore’un Count’u 4 olduğundan ilk 4 araba için Semaphore’ları alarak kapıyı 4 kere açacak, sonraki araçlar için ise Semaphore boş kaldığından Semaphore dolana kadar işlemci tarafından uyutulacaktır. Çıkış kapısından araçlar çıktıkça (yani Semaphore Release edildikçe) giriş kapısını kontrol eden Thread işlemci tarafından uyandırılacak, Semaphore’u tekrar alabilecek ve çıkan araç kadar yeni araç girişine izin verecektir.

Bu noktada benzer amaçlar (Critical Section oluşturmak) için kullanılabilecek Mutex ile Count'u 1 olan Binary Semaphore arasındaki farkı da açıklamak gerekir. Mutex'ler Critical Section yönetimi sağlamak üzere tasarlandığı için kendilerini Release eden Thread'in alan Thread olmasını gerekli tutmaktadır, Semaphore'da ise böyle bir kısıtlama yoktur hatta birçok durumda Semaphore'u bırakan Thread alan Thread'den farklıdır.

Özetlemek gerekirse Semaphore’lar aralarında sinyalleşme gereken Producer-Consumer (Üretici-Tüketici) senaryolarında sinyalleşme açısından muhteşem bir çözüm sunmaktadır.

Otopark örneğinde kapıyı açan Thread’in Semaphore dolu iken Semaphore’u almak istediğinde işlemci tarafından uyutulduğunu söylemiştik. İşlemcinin ilgili Thread’in bütün state’ini saklaması ve uyandığında tekrar geri getirmesi (Context-Switch) çok maliyetli bir işlem olduğu için Semaphore’un çabucak bırakılacağının tahmin edildiği senaryolarda Thread uyutulmaz, bir süre canlı tutularak Semaphore bırakılana kadar birkaç boş tur (Busy Waiting) attırılır. Bu tür Semaphore’lara Spinlock Semaphore denir ve düşük seviyeli işlemlerde (Device Driver, Linux Kernel, vb) sıklıkla etkili bir şekilde kullanılırlar. Spinlock Semaphore’da Thread belirli bir süre beklediği Semaphore’u alamazsa normal bir Thread gibi işlemci tarafından beklemeye alınır. Tahmin edebileceğiniz gibi Spinlock Semaphore’lar Multi-Core işlemcilerde anlamlı hale gelirler.

Bu flood sonunda umarım multithread bir uygulamada aynı log dosyasına aşırı loglamanın uygulama performansını neden kötü etkilediği anlaşılmıştır.

Yukarıdaki bilgiler ışığında, Nasa’nın Mars keşif aracı PathFinder’ın başından geçen Semaphore’larla ilgili tatsız olayı aşağıdaki yazıdan bir kez daha okumanızı tavsiye ederim :-) https://medium.com/@gokhansengun/mars-ke%C5%9Fif-arac%C4%B1-pathfinderdaki-i%CC%87lgin%C3%A7-yaz%C4%B1l%C4%B1m-problemi-5b6ebe771d55