Semaphore: nedir, nasıl çalışır ve iş parçacığı senkronizasyonunda kullanımı

Yazar: IT Sectr Yayınlanma: 2026-03-19 Okuma süresi: 8 dk

Semaphore, bir sayaç ve bekleyen iş parçacıkları kuyruğu aracılığıyla paylaşılan bir kaynağa erişimi kontrol eden bir senkronizasyon ilkelidir. Wikipedia, 2024'e göre, semafor 1965 yılında Edsger Dijkstra tarafından çoklu iş parçacığı etkileşimi sorunlarını çözmek için önerilmiştir. Araç, kritik bir bölümle aynı anda çalışan iş parçacığı sayısını sınırlamaya olanak tanır.

Ana Noktalar

  • Semaphore, bir izin sayacı aracılığıyla erişimi kontrol eden bir senkronizasyon ilkelidir.
  • İkili semafor 0 ve 1 değerlerini alır ve bir engelleme bayrağı olarak çalışır.
  • Sayısal semafor, belirtilen sayıda iş parçacığının aynı anda erişmesine izin verir.
  • Mutex'in aksine, semafor bir sahip iş parçacığına bağlı değildir.
  • Deadlock, semaforların yanlış kullanımında ana tehlikelerden biridir.

Semaphore Nedir?

Semaphore, paylaşılan bir kaynağa erişimi kontrol etmek için bir sayaç kullanan bir senkronizasyon ilkelidir. Bu kavram, 1965 yılında Edsger Dijkstra tarafından önerilmiş ve işletim sistemlerindeki tüm modern senkronizasyon mekanizmalarının temeli olmuştur.

Tanım ve Amaç

Semafor, iki atomik işleme sahip bir tamsayı değişkendir: wait (acquire) ve signal (release). Wait işlemi sayacı azaltırken, signal işlemi sayacı artırır. Sayaç sıfıra ulaştığında, wait'i çağıran iş parçacığı başka bir iş parçacığı signal yürütünceye kadar bloke edilir.

Bir semaforun temel amacı, kritik bölümleri birden çok iş parçacığının aynı anda erişiminden korumaktır. Mutex'in aksine, semafor bir sahip iş parçacığına bağlanma gerektirmez, bu da onu daha geniş bir koordinasyon görevi yelpazesi için uygun hale getirir.

Tarih ve Teorik Temel

Semafor kavramı, Technische Hogeschool Eindhoven'da geliştirilen THE işletim sistemi bağlamında ortaya çıkmıştır. Dijkstra, semaforu matematiksel bir soyutlama olarak biçimlendirmiş ve herhangi bir senkronizasyon ilkelini uygulamak için yeterliliğini kanıtlamıştır.

Semafor Nasıl Çalışır?

Semafor mekanizması, iki atomik işlem ve bir iç bekleme kuyruğuna dayanır. Acquire çağrıldığında, iş parçacığı sayaç değerini kontrol eder ve ya yürütmeye devam eder ya da kaynak serbest bırakılana kadar bloke olur.

Sayaç ve Atomik İşlemler

Bir semafor oluştururken, izin sayacının bir başlangıç değeri belirlenir. Her acquire çağrısı sayacı 1 azaltır. Bundan sonra sayaç negatif olursa, iş parçacığı bloke edilir. Release işlemi sayacı artırır ve bekleyen iş parçacıklarından birini uyandırır.

kotlin
import java.util.concurrent.Semaphore

val semaphore = Semaphore(3)

fun accessResource() {
    semaphore.acquire()
    try {
        println("${Thread.currentThread().name} çalışıyor")
    } finally {
        semaphore.release()
    }
}

Bekleme Kuyruğu ve Zamanlama

Bir iş parçacığı sıfır sayaçla acquire çağırdığında, işletim sistemi onu semaforun FIFO kuyruğuna yerleştirir. İş parçacığı BLOCKED durumuna geçer ve CPU zamanı tüketmez. Bir release çağrısından sonra, kuyruktaki ilk iş parçacığı RUNNABLE durumuna geçer ve kaynağa erişim sağlar.

Semafor Türleri

Senkronizasyon teorisinde, iki ana semafor türü ayırt edilir: ikili ve sayısal. Türün seçimi, kaynaklara erişim yönetiminin belirli görevine bağlıdır.

İkili Semafor (Binary Semaphore)

İkili semafor yalnızca 0 ve 1 değerlerini alır. Davranış olarak mutex'e benzer, ancak sahiplik gereksinimi olmadan — herhangi bir iş parçacığı release yürütebilir. Bu tür semaforlar, iş parçacıkları arasında hazır olma bayrakları ve olayları uygulamak için uygundur.

kotlin
val ready = Semaphore(0)

fun producer() {
    Thread.sleep(1000)
    ready.release()
}

fun consumer() {
    ready.acquire()
    println("Veriler hazır")
}

Sayısal Semafor (Counting Semaphore)

Sayısal semafor negatif olmayan herhangi bir değeri alabilir. Birden çok örneğin bulunduğu benzer kaynaklardan oluşan bir havuzu yönetmek için kullanılır. Örneğin, 5 ağ bağlantısından oluşan bir havuz: her acquire bir bağlantı alır, release onu havuza geri verir.

Sayısal semaforlar, harici hizmetlere erişim hızını sınırlamak ve iş parçacığı havuzları uygulamak için vazgeçilmezdir. Manuel iş parçacığı yönetimi olmadan paralellik derecesini hassas bir şekilde kontrol etmeye olanak tanırlar.

Parametreİkili SemaforSayısal Semafor
Aralık0 veya 10'dan N'ye
Eşzamanlı iş parçacığı1N'e kadar
Uygulamasinyalleşme, bayraklarkaynak havuzları, hız sınırlama

Semaphore vs Mutex

Geliştiriciler genellikle semafor ve mutex'i karıştırır, ancak aralarında temel farklılıklar vardır. Bu farklılıkları anlamak, bir projede doğru senkronizasyon mekanizmasını seçmek için kritik öneme sahiptir.

Sahiplik İlkesi

Temel fark sahiplik kavramıdır. Bir mutex her zaman hangi iş parçacığının onu aldığını bilir ve yalnızca o iş parçacığı onu serbest bırakabilir. Bir semaforun sahibi yoktur: herhangi bir iş parçacığı acquire çağırmadan bile release çağırabilir. Bu, mutex'i veri koruması için daha güvenli ve semaforu koordinasyon için daha esnek hale getirir.

Performans ve Kullanım Senaryoları

Pratikte, mutex tipik senaryolar için optimizasyonlar sayesinde basit karşılıklı dışlamada daha hızlıdır. Semafor, sayacı korumak için ek yük gerektirir. Ancak, paralelliği sınırlamak veya üretici-tüketici modelini uygulamak için semafor vazgeçilmezdir.

ÖzellikSemaphoreMutex
Sahipliksahip yoksahibi var
Serbest bırakmaherhangi bir iş parçacığıyalnızca sahip iş parçacığı
Sayaç0'dan N'yeikili
Kullanım durumuparalellik sınırlama ve sinyalleşmekritik bölüm koruması
Yinelemehayırevet (yeniden girebilir)

Mobil Geliştirmede Semaphore

Mobil uygulama geliştirmede, Semaphore sınırlı kaynaklara (ağ bağlantıları, dosyalar, veritabanları ve donanım bileşenleri) erişimi yönetmek için kullanılır. Modern platformlar kullanışlı yerleşik uygulamalar sağlar.

Sunucu Bağlantı Havuzu

Tipik bir kullanım durumu HTTP bağlantı havuzudur. Bir uygulama, sağlayıcının API'si paralelliği sınırladığı için bir sunucuya aynı anda en fazla 4 istek gönderebilir. Başlangıç değeri 4 olan bir semafor, herhangi bir yük altında eşzamanlı istek sayısının limiti aşmamasını sağlarken, diğer iş parçacıkları kuyrukta bekler.

Semafor olmadan, kullanıcı etkinliğindeki keskin bir artış sunucu altyapısında ani aşırı yüklenmeye neden olabilir, bu da zaman aşımlarına ve 429 Too Many Requests hatalarına yol açar. Semaphore bir sigorta gibi çalışır, aktif iş parçacığı sayısından bağımsız olarak kesin olarak belirtilen sayıda eşzamanlı çağrıya izin verir.

Android için Kotlin'de Semaphore

Android, java.util.concurrent paketinden Semaphore sınıfını sağlar. Sunucu aşırı yüklenmesini önlemek için eşzamanlı ağ isteklerini iki iş parçacığıyla sınırlama örneğine bakalım.

kotlin
class ApiClient {
    private val throttle = Semaphore(2)

    suspend fun fetch(url: String): Result {
        throttle.acquire()
        return try {
            httpGet(url)
        } finally {
            throttle.release()
        }
    }
}

iOS için Swift'te DispatchSemaphore

iOS'te, GCD'den DispatchSemaphore aynı görevi çözer. Geliştiriciler, ana iş parçacığını bloke etmeden eşzamansız kodda kaynaklara erişimi senkronize etmek için kullanır.

swift
let semaphore = DispatchSemaphore(value: 3)

func processBatch(_ items: [UIImage]) {
    for img in items {
        semaphore.wait()
        DispatchQueue.global().async {
            applyFilter(to: img)
            semaphore.signal()
        }
    }
}

Semaforlarla Çalışırken Tipik Hatalar

En yaygın hata, bir istisna oluştuğunda unutulan release'dir. Bir iş parçacığı release çağırmadan önce bir hatayla sonlanırsa, semafor diğer iş parçacıkları için kalıcı olarak bloke kalır. Garantili serbest bırakma için try/finally veya defer kullanın. İkinci sorun, farklı iş parçacıkları tarafından farklı sırada birden çok semafor alırken deadlock'tur.

Semafor Kullanım Modelleri

Semaforlar yalnızca veri koruması için değil, aynı zamanda karmaşık çok iş parçacıklı senaryolarda iş parçacığı koordinasyonu için de kullanılır. Yaygın modelleri bilmek geliştirmeyi hızlandırır ve senkronizasyon hataları olasılığını azaltır.

Gerçek projelerde semafor kullanımının birkaç kanıtlanmış modeli vardır. Bunları bilmek, tipik hatalardan kaçınmaya ve güvenilir çok iş parçacıklı sistemler oluşturmaya yardımcı olur.

Hız Sınırlayıcı (Rate Limiter)

Başlangıç değeri N ve bir zamanlayıcı aracılığıyla periyodik release ile bir semafor, API isteklerinin hız sınırlamasını uygular. Örneğin, bir hizmet saniyede 10 isteğe izin verir: semafor 10'da başlar, her istek sayacı azaltır ve ayrı bir TimerTask her saniye sayacı başlangıç değerine döndürür. Bu, hem uygulamayı hem de sunucuyu aşırı yüklenmeden korur.

Semaforlarla Üretici-Tüketici

Klasik üretici-tüketici probleminde, iki semafor bir arabelleği yönetir: empty (yazma izinleri) ve full (okuma izinleri). Üretici empty'de acquire ve full'de release çağırırken, tüketici tersini yapar. Bu şema, tüketicinin asla boş bir arabellek okumamasını ve üreticinin asla onu taşırmamasını garanti eder.

Aynı şema, işletim sistemlerindeki sınırlı arabelleğin — sabit boyutlu bir halka arabelleğin temelini oluşturur. Mobil uygulamalarda, model görüntü, video dosyası ve analitik olay kuyruklarını işlemek için kullanılır.

Ağ İsteklerini Sınırlama

Semaforlar, arka plan hizmetlerinde ağ çağrılarını sınırlamak için başarıyla kullanılır. Örneğin, bir analitik uygulaması sunucuya olay paketleri gönderir. Yoğun yük zamanlarında (uygulama başlatma, çevrimdışı sonrası senkronizasyon) eşzamanlı iş parçacıklarını sınırlamadan, eşzamanlı istek sayısı sunucu limitlerini aşabilir. Başlangıç değeri 3 olan bir semafor, sorunsuz gönderme sağlar ve sunucu tarafında blokajı önler.

Sıkça Sorulan Sorular

Semaphore ve normal sayaç arasındaki fark nedir?

Semaphore yalnızca bir sayaç değil, atomik işlemleri ve bekleme kuyruğu olan bir senkronizasyon ilkelidir. Normal bir sayaç, iş parçacığını bloke etmez ve birden çok iş parçacığı eşzamanlı eriştiğinde atomik artışı garanti etmez.

Semafor deadlock'a neden olabilir mi?

Evet, farklı iş parçacıkları tarafından farklı sırada birden çok semafor alındığında deadlock mümkündür. Örneğin, iş parçacığı A önce S1'i, sonra S2'yi alırken, iş parçacığı B önce S2'yi, sonra S1'i alır. Projedeki tüm semaforlar için tek bir alma sırası belirleyin.

Sayaç sıfırken acquire çağrıldığında ne olur?

İş parçacığı bloke olur ve bekleme durumuna girer. Başka bir iş parçacığı release çağırana kadar CPU zamanı tüketmez. Java'da bu BLOCKED durumudur; Swift'te iş parçacığı GCD tarafından askıya alınır.

Binary Semaphore temelde Mutex'ten nasıl farklıdır?

Temel fark sahipliktir. Bir Mutex yalnızca sahip iş parçacığı tarafından serbest bırakılabilir. Bir Binary Semaphore herhangi bir iş parçacığı tarafından serbest bırakılabilir, bu iş parçacıkları arasında sinyalleşme için uygundur ancak veri bütünlüğünü korumak için daha az güvenlidir.

Sayaç için hangi başlangıç değeri seçilmelidir?

Başlangıç değeri senaryoya bağlıdır. Tek bir kaynağı korumak için — 1. N bağlantılı bir havuz için — N. İş parçacıkları arasında sinyalleşme için, tüketici iş parçacığının üreticiden sinyal beklemesi için 0 kullanın.

Özet

  • Semaphore, 1965'te Dijkstra tarafından önerilen sayaç tabanlı bir senkronizasyon ilkelidir.
  • İkili semafor 0 ve 1 değerlerini alır; sayısal semafor negatif olmayan herhangi bir değeri alır.
  • Acquire ve release işlemleri atomiktir ve iş parçacığı güvenlidir.
  • Mutex'in aksine, semafor bir sahip iş parçacığına bağlı değildir.
  • Sayısal semaforlar kaynak havuzlarını yönetmek ve paralelliği sınırlamak için kullanılır.
  • Unutulan release, iş parçacıklarının askıda kalmasına yol açan en yaygın hatadır.

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun