Semaphore: bu nədir, iş prinsipi və thread sinxronizasiyasında tətbiqi

Müəllif: IT Sectr Dərc olunub: 2026-03-19 Oxuma vaxtı: 8 dəq

Semaphore — sinxronizasiya primitividir, sayğac və gözləyən thread-lar növbəsi vasitəsilə ümumi resursa girişi idarə edir. Wikipedia, 2024-ə görə, semafor Edsger Dijkstra tərəfindən 1965-ci ildə çoxiplikli qarşılıqlı əlaqə problemlərini həll etmək üçün təklif edilmişdir. Alət kritik bölmə ilə eyni vaxtda işləyən thread-ların sayını məhdudlaşdırmağa imkan verir.

Əsas məqamlar

  • Semaphore — icazə sayğacı vasitəsilə girişi idarə edən sinxronizasiya primitividir.
  • Binary semafor 0 və 1 qiymətlərini alır, bloklama bayrağı kimi işləyir.
  • Sayğac semaforu müəyyən sayda thread-ın eyni vaxtda girişinə icazə verir.
  • Mutexdən fərqli olaraq, semafor sahib thread-a bağlı deyil.
  • Deadlock — semaforların səhv istifadəsi zamanı əsas təhlükələrdən biridir.

Semaphore nədir?

Semaphore — paylaşılan resursa girişi idarə etmək üçün sayğacdan istifadə edən sinxronizasiya primitividir. Konsepsiya Edsger Dijkstra tərəfindən 1965-ci ildə təklif edilmiş və əməliyyat sistemlərində bütün müasir sinxronizasiya mexanizmlərinin əsasına çevrilmişdir.

Tərif və məqsəd

Semafor iki atomik əməliyyatı olan tamədədli dəyişəndir: wait (acquire) və signal (release). Wait əməliyyatı sayğacı azaldır, signal isə artırır. Sayğac sıfıra êtdıqda, wait-i çağıran thread başqa thread tərəfindən signal yerinə yetirilənə qədər bloklanır.

Semaforun əsas məqsədi kritik bölmələri bir neçə thread-ın eyni vaxtda girişindən qorumaqdır. Mutexdən fərqli olaraq, semafor sahib thread-a bağlanmağı tələb etmir, bu da onu daha geniş əlaqələndirmə tapşırıqları üçün əlverişli edir.

Tarix və nəzəri əsas

Semafor konsepsiyası Technische Hogeschool Eindhoven-də hazırlanmış THE əməliyyat sistemi kontekstində yaranmışdır. Dijkstra semaforu riyazi abstraksiya kimi formalizasiya etmiş, onun istənilən sinxronizasiya primitivlərini həyata keçirmək üçün kifayət olduğunu sübut etmişdir.

Semafor necə işləyir?

Semafor mexanizmi iki atomik əməliyyat və daxili gözləmə növbəsi üzərində qurulub. Acquire çağırışı zamanı thread sayğacın dəyərini yoxlayır və ya icraya davam edir, ya da resurs boşalana qədər bloklanır.

Sayğac və atomik əməliyyatlar

Semafor yaradılarkən ilkin icazə sayğacı dəyəri təyin edilir. Hər acquire çağırışı sayğacı 1 azaldır. Bundan sonra sayğac mənfi olarsa, thread bloklanır. Release əməliyyatı sayğacı artırır və gözləyən thread-lardan birini oyadır.

kotlin
import java.util.concurrent.Semaphore

val semaphore = Semaphore(3)

fun accessResource() {
    semaphore.acquire()
    try {
        println("${Thread.currentThread().name} işləyir")
    } finally {
        semaphore.release()
    }
}

Gözləmə növbəsi və planlaşdırma

Thread sıfır sayğacla acquire çağırdıqda, ƏS onu semaforun FIFO növbəsinə yerləşdirir. Thread BLOCKED vəziyyətinə keçir, prosessor vaxtı sərf etmir. Release çağırışından sonra növbədəki ilk thread RUNNABLE vəziyyətinə keçir və resursa giriş əldə edir.

Semafor növləri

Sinxronizasiya nəzəriyyəsində semaforların iki əsas növü fərqləndirilir: binary (ikili) və counting (sayğac). Növün seçimi resurslara girişin idarə edilməsinin konkret tapşırığından asılıdır.

Binary Semaphore (İkili semafor)

Binary semafor yalnız 0 və 1 qiymətlərini alır. Davranışına görə mutexi xatırladır, lakin sahiblik tələbi olmadan — istənilən thread release edə bilər. Belə semaforlar thread-lar arasında hazırlıq bayraqları və hadisələri həyata keçirmək üçün əlverişlidir.

kotlin
val ready = Semaphore(0)

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

fun consumer() {
    ready.acquire()
    println("Məlumatlar hazırdır")
}

Counting Semaphore (Sayğac semaforu)

Sayğac semaforu istənilən qeyri-mənfi qiymət ala bilər. Bir neçə nüsxəsi mövcud olan eynitipli resurslar hovuzunu idarə etmək üçün istifadə olunur. Məsələn, 5 şəbəkə bağlantısı hovuzu: hər acquire bir bağlantını tutur, release onu hovuza qaytarır.

Sayğac semaforları xarici xidmətlərə giriş sürətini məhdudlaşdırmaq və thread hovuzlarını həyata keçirmək üçün əvəzolunmazdır. Onlar thread-ları əl ilə idarə etmədən paralellik dərəcəsini dəqiq nəzarət etməyə imkan verir.

ParametrBinary semaforSayğac semaforu
Diapazon0 və ya 10-dan N-ə qədər
Eyni vaxtda thread-lar1N-ə qədər
Tətbiqsiqnalizasiya, bayraqlarresurs hovuzları, rate limiting

Semaphore vs Mutex

Tərtibatçılar tez-tez semafor və mutexi qarışdırırlar, baxmayaraq ki, aralarında fundamental fərqlər var. Bu fərqləri anlamaq layihədə düzgün sinxronizasiya mexanizmini seçmək üçün kritik əhəmiyyət daşıyır.

Sahiblik prinsipi

Əsas fərq — sahiblik anlayışı. Mutex həmişə onu hansı threadın tutduğunu bilir və yalnız həmin thread onu sərbəst buraxa bilər. Semaforun sahibi yoxdur: istənilən thread acquire çağırmadan da release edə bilər. Bu, mutexi məlumatların qorunması üçün daha təhlükəsiz, semaforu isə koordinasiya üçün daha çevik edir.

Performans və istifadə ssenariləri

Təcrübədə mutex tipik ssenari üçün optimallaşdırmalar sayəsində sadə qarşılıqlı bloklama üçün daha sürətlidir. Semafor sayğacın saxlanması üçün əlavə yük tələb edir. Bununla belə, paralelliyi məhdudlaşdırmaq və ya “istehsalçı-istehlakçı” nümunəsini həyata keçirmək üçün semafor əvəzolunmazdır.

XüsusiyyətSemaphoreMutex
Sahibliksahib yoxdursahib var
Azad etməistənilən threadyalnız sahib thread
Sayğac0-dan N-ə qədərbinary
Use caseparalelliyin məhdudlaşdırılması və siqnalizasiyakritik bölmənin qorunması
Rekursiyayoxbəli (reentrant)

Semaphore-un mobil inkişafda tətbiqi

Mobil tətbiqlərin hazırlanmasında Semaphore məhdud resurslara girişi idarə etmək üçün istifadə olunur: şəbəkə bağlantıları, fayllar, verilənlər bazaları və aparat komponentləri. Müasir platformalar rahat daxili tətbiqlər təqdim edir.

Server ilə bağlantılar hovuzu

Tipik hallardan biri HTTP bağlantıları hovuzudur. Tətbiq serverə eyni vaxtda 4-dən çox sorğu göndərə bilməz, çünki provayderin API-i paralelliyi məhdudlaşdırır. İlkin dəyəri 4 olan semafor istənilən yük altında eyni vaxtda sorğuların sayının limiti keçməyəcəyinə zəmanət verir, qalan thread-lar növbədə gözləyir.

Semafor olmadan istifadəçi aktivliyinin kəskin artması zamanı server infrastrukturu qəfil həddindən artıq yüklənə bilər, bu da timeoutlara və 429 Too Many Requests səhvlərinə səbəb olur. Semafor aktiv thread-ların sayından asılı olmayaraq ciddi şəkildə müəyyən edilmiş sayda eyni vaxtlı çağırışları buraxan qoruyucu kimi işləyir.

Android üçün Kotlin-də Semaphore

Android java.util.concurrent paketindən Semaphore sinfini təqdim edir. Serverin həddindən artıq yüklənməsinin qarşısını almaq üçün eyni vaxtda şəbəkə sorğularını iki threadla məhdudlaşdırmaq nümunəsinə baxaq.

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

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

iOS üçün Swift-də DispatchSemaphore

iOS-də GCD-dən DispatchSemaphore eyni tapşırığı həll edir. Tərtibatçılar onu asinxron koddakı resurslara girişi sinxronlaşdırmaq üçün əsas thread-ı bloklamadan istifadə edirlə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 işləyərkən tipik səhvlər

Ən çox yayılmış səhv — istisna zamanı unutulmuş release-dir. Thread release çağırmadan xəta ilə bitərsə, semafor qalan thread-lar üçün əbədi olaraq bloklanmış qalır. Zəmanətli sərbəst buraxma üçün try/finally və ya defer istifadə edin. İkinci problem — müxtəlif thread-lar tərəfindən fərqli sırada bir neçə semaforun tutulması zamanı deadlock-dur.

Semaforların istifadə nümunələri

Semaforlar təkcə məlumatların qorunması üçün deyil, həm də mürəkkəb çoxiplikli ssenarilərdə thread-ların koordinasiyası üçün tətbiq olunur. Ümumi nümunələri bilmək inkişafı sürətləndirir və sinxronizasiya səhvləri ehtimalını azaldır.

Real layihələrdə semaforların tətbiqinin bir neçə sübut olunmuş nümunəsi mövcuddur. Onları bilmək tipik səhvlərdən qaçmağa və etibarlı çoxiplikli sistemlər qurmağa kömək edir.

Rate Limiter (sürət məhdudlaşdırıcısı)

İlkin dəyəri N olan semafor və timer vasitəsilə dövri release API-ə sorğuların sürət məhdudlaşdırılmasını həyata keçirir. Məsələn, xidmət saniyədə 10 sorğuya icazə verir: semafor 10 ilə başlayır, hər sorğu sayğacı azaldır, ayrıca TimerTask saniyədə bir dəfə sayğacı ilkin dəyərinə qaytarır. Bu, həm tətbiqi, həm də serveri həddindən artıq yükdən qoruyur.

İstehsalçı-İstehlakçı semaforlar vasitəsilə

Klassik istehsalçı-istehlakçı tapşırığında iki semafor buferi idarə edir: empty (yazma icazəsi) və full (oxuma icazəsi). İstehsalçı empty-də acquire və full-da release çağırır, İstehlakçı — əksinə. Bu sxem İstehlakçının heç vaxt boş buferi oxumayacağına, İstehsalçının isə onu daşdırmayacağına zəmanət verir.

Eyni sxem əməliyyat sistemlərində məhdud buferin — sabit ölçülü halqa buferinin əsasını təşkil edir. Mobil tətbiqlərdə bu nümunə şəkillər, video fayllar və analitik hadisələr növbələrini emal etmək üçün istifadə olunur.

Şəbəkə sorğularının throttling-i

Semaforlar fon xidmətlərində şəbəkə çağırışlarının throttling-i üçün uğurla istifadə olunur. Məsələn, analitika tətbiqi serverə hadisə paketləri göndərir. Pik yüklərdə (tətbiqin işə salınması, oflayn sinxronizasiya) eyni vaxtda sorğuların sayı server limitlərini keçə bilər. İlkin dəyəri 3 olan semafor hamar göndərməyə zəmanət verir və server tərəfində blokadanın qarşısını alır.

Tez-tez verilən suallar

Semaphore ilə adi sayğac arasında nə fərq var?

Semafor sadəcə sayğac deyil, atomik əməliyyatları və gözləmə növbəsi olan sinxronizasiya primitividir. Adi sayğac thread-ı bloklamır və bir neçə thread-ın rəqabətli girişi zamanı inkrementin atomikliyinə zəmanət vermir.

Semafor deadlock-a səbəb ola bilərmi?

Bəli, deadlock müxtəlif thread-lar tərəfindən semaforların fərqli sırada tutulması zamanı mümkündür. Məsələn, thread A S1-i, sonra S2-ni, thread B isə S2-ni, sonra S1-i tutur. Layihədə bütün semaforlar üçün vahid tutma sırası təyin edin.

Sıfır sayğacla acquire zamanı nə baş verir?

Thread bloklanır və gözləmə vəziyyətinə keçir. Başqa thread release çağırana qədər prosessor vaxtı sərf etmir. Java-da bu BLOCKED vəziyyətidir, Swift-də thread GCD tərəfindən dayandırılır.

Binary Semaphore Mutex-dən əsaslı şəkildə nə ilə fərqlənir?

Əsas fərq — sahiblik. Mutex yalnız sahib thread tərəfindən sərbəst buraxıla bilər. Binary Semaphore istənilən thread tərəfindən sərbəst buraxıla bilər, bu thread-lar arasında siqnalizasiya üçün əlverişlidir, lakin məlumatların bütövlüyünü qorumaq üçün daha az təhlükəsizdir.

Sayğac üçün hansı ilkin dəyəri seçməli?

İlkin dəyər ssenaridən asılıdır. Bir resursu qorumaq üçün — 1. N bağlantıdan ibarət hovuz üçün — N. Thread-lar arasında siqnalizasiya üçün 0 istifadə edin ki, istehlakçı thread istehsalçıdan siqnal gözləsin.

Xülasə

  • Semaphore — 1965-ci ildə Dijkstra tərəfindən təklif edilmiş sayğaca əsaslanan sinxronizasiya primitividir.
  • Binary semafor 0 və 1 qiymətlərini alır, sayğac semaforu — istənilən qeyri-mənfi qiyməti.
  • Acquire və release əməliyyatları atomik və thread-təhlükəsizdir.
  • Mutexdən fərqli olaraq, semafor sahib thread-a bağlı deyil.
  • Sayğac semaforları resurs hovuzlarını idarə etmək və paralelliyi məhdudlaşdırmaq üçün istifadə olunur.
  • Unudulmuş release — thread-ların donmasına səbəb olan ən geniş yayılmış səhvdir.

Açar təslim mobil tətbiq hazırlayacağıq

IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.

Layihəni müzakirə et

Həm də oxuyun