Semaphore: ano ito, prinsipyo ng paggawa at aplikasyon sa pag-sync ng thread

May-akda: IT Sectr Nai-publish: 2026-03-19 Oras ng pagbabasa: 8 min

Ang Semaphore ay isang synchronization primitive na namamahala ng access sa shared resource sa pamamagitan ng counter at queue ng mga naghihintay na thread. Ayon sa Wikipedia, 2024, ang semaphore ay iminungkahi ni Edsger Dijkstra noong 1965 para malutas ang mga problema ng multithread interaction. Ang tool ay nagpapahintulot na limitahan ang bilang ng mga thread na sabay na gumagana sa critical section.

Mga Pangunahing Punto

  • Semaphore — synchronization primitive na namamahala ng access sa pamamagitan ng permission counter.
  • Binary semaphore ay kumukuha ng mga halaga 0 at 1, gumagana bilang lock flag.
  • Counting semaphore ay nagpapahintulot ng sabay na access ng isang tiyak na bilang ng mga thread.
  • Hindi tulad ng mutex, ang semaphore ay hindi nakatali sa may-ari na thread.
  • Deadlock — isa sa mga pangunahing panganib sa maling paggamit ng semaphores.

Ano ang Semaphore?

Semaphore — ay isang synchronization primitive na gumagamit ng counter para pamahalaan ang access sa shared resource. Ang konsepto ay iminungkahi ni Edsger Dijkstra noong 1965 at naging pundasyon ng lahat ng modernong synchronization mechanism sa operating system.

Depinisyon at Layunin

Ang semaphore ay isang integer variable na may dalawang atomic operations: wait (acquire) at signal (release). Ang wait operation ay nagpapababa ng counter, at ang signal ay nagpapataas nito. Kapag ang counter ay umabot sa zero, ang thread na tumatawag ng wait ay blocked hanggang sa maisagawa ang signal ng ibang thread.

Ang pangunahing layunin ng semaphore ay protektahan ang critical sections mula sa sabay na access ng maraming thread. Hindi tulad ng mutex, ang semaphore ay hindi nangangailangan ng pagkakatali sa may-ari na thread, na ginagawa itong angkop para sa mas malawak na hanay ng coordination tasks.

Kasaysayan at Teoretikal na Batayan

Ang konsepto ng semaphore ay lumitaw sa konteksto ng operating system na THE, na binuo sa Technische Hogeschool Eindhoven. Pormalisado ni Dijkstra ang semaphore bilang isang mathematical abstraction, na pinatunayan ang sapat nito para sa pagpapatupad ng anumang synchronization primitives.

Paano gumagana ang semaphore?

Ang mekanismo ng semaphore ay batay sa dalawang atomic operations at isang internal waiting queue. Kapag tumatawag ng acquire, sinusuri ng thread ang halaga ng counter at maaaring magpatuloy sa pag-execute o ma-block hanggang sa ma-release ang resource.

Counter at Atomic Operations

Kapag gumagawa ng semaphore, ang initial value ng permission counter ay itinakda. Bawat pagtawag ng acquire ay nagpapababa ng counter ng 1. Kung pagkatapos nito ang counter ay maging negatibo, ang thread ay ma-block. Ang release operation ay nagpapataas ng counter at gumigising ng isa sa mga naghihintay na thread.

kotlin
import java.util.concurrent.Semaphore

val semaphore = Semaphore(3)

fun accessResource() {
    semaphore.acquire()
    try {
        println("${Thread.currentThread().name} gumagana")
    } finally {
        semaphore.release()
    }
}

Waiting Queue at Pag-iskedyul

Kapag ang thread ay tumatawag ng acquire na may counter na zero, inilalagay ito ng OS sa FIFO queue ng semaphore. Ang thread ay napupunta sa BLOCKED state, hindi gumagamit ng processor time. Pagkatapos ng release call, ang unang thread sa queue ay napupunta sa RUNNABLE state at nakakakuha ng access sa resource.

Mga uri ng semaphore

Sa synchronization theory, dalawang pangunahing uri ng semaphore ang nakikilala: binary at counting. Ang pagpili ng uri ay depende sa partikular na gawain ng pamamahala ng access sa resources.

Binary Semaphore

Ang binary semaphore ay kumukuha lamang ng mga halaga 0 at 1. Sa pag-uugali, ito ay kahawig ng mutex, ngunit walang kinakailangan ng pagmamay-ari — anumang thread ay maaaring mag-execute ng release. Ang ganitong mga semaphore ay maginhawa para sa pagpapatupad ng mga readiness flag at inter-thread na mga event.

kotlin
val ready = Semaphore(0)

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

fun consumer() {
    ready.acquire()
    println("Ang data ay handa")
}

Counting Semaphore

Counting semaphore ay maaaring kumuha ng anumang non-negative na halaga. Ito ay ginagamit para sa pamamahala ng pool ng mga homogenous na resource kung saan maraming instance ang available. Halimbawa, isang pool ng 5 network connections: bawat acquire ay kumukuha ng isang connection, ang release ay nagbabalik nito sa pool.

Ang counting semaphores ay kailangang-kailangan sa paglilimita ng bilis ng access sa external services at pagpapatupad ng thread pools. Pinapayagan nila ang tumpak na kontrol ng antas ng parallelism nang walang manual na pamamahala ng thread.

ParameterBinary SemaphoreCounting Semaphore
Saklaw0 o 1mula 0 hanggang N
Sabay na thread1hanggang N
Aplikasyonsignaling, mga flagresource pools, rate limiting

Semaphore vs Mutex

Ang mga developer ay madalas na nagkakamali ng semaphore at mutex, kahit na may mga pundamental na pagkakaiba sa pagitan nila. Ang pag-unawa sa mga pagkakaibang ito ay mahalaga para sa pagpili ng tamang synchronization mechanism sa proyekto.

Prinsipyo ng Pagmamay-ari

Ang pangunahing pagkakaiba — konsepto ng pagmamay-ari. Ang mutex ay laging alam kung aling thread ang kumuha nito, at tanging ang thread na iyon ang makakapag-release nito. Ang semaphore ay walang may-ari: anumang thread ay maaaring tumawag ng release, kahit na hindi tumatawag ng acquire. Ginagawa nitong mas ligtas ang mutex para sa proteksyon ng data, at ang semaphore — mas flexible para sa koordinasyon.

Pagganap at mga Sitwasyon ng Paggamit

Sa praktika, ang mutex ay mas mabilis para sa simpleng mutual locking dahil sa mga optimization para sa tipikal na sitwasyon. Ang semaphore ay nangangailangan ng karagdagang overhead para sa pagpapanatili ng counter. Gayunpaman, para sa paglilimita ng parallelism o pagpapatupad ng pattern na “producer-consumer”, ang semaphore ay kailangang-kailangan.

KatangianSemaphoreMutex
Pagmamay-ariwalang may-arimay may-ari
Pag-releaseanumang threadtanging may-ari na thread
Countermula 0 hanggang Nbinary
Use casepaglilimita ng parallelism at signalingproteksyon ng critical section
Recursionhindioo (reentrant)

Aplikasyon ng Semaphore sa mobile development

Sa pag-develop ng mobile applications, ang Semaphore ay ginagamit para sa pamamahala ng access sa limitadong resources: network connections, files, databases, at hardware components. Ang mga modernong platform ay nagbibigay ng mga maginhawang built-in na implementasyon.

Pool ng mga Koneksyon sa Server

Isa sa mga tipikal na kaso — pool ng HTTP connections. Ang aplikasyon ay maaaring magpadala ng hindi hihigit sa 4 na sabay na request sa server, dahil ang API ng provider ay naglilimita ng parallelism. Ang semaphore na may initial value na 4 ay ginagarantiyahan na sa anumang load, ang bilang ng sabay na request ay hindi lalampas sa limit, at ang ibang threads ay maghihintay sa queue.

Kung walang semaphore, sa biglaang pagtaas ng aktibidad ng user, ang server infrastructure ay maaaring makaranas ng biglaang overload, na humahantong sa timeouts at errors 429 Too Many Requests. Ang semaphore ay gumagana tulad ng isang fuse, na nagpapahintulot ng mahigpit na tinukoy na bilang ng sabay na tawag anuman ang bilang ng aktibong threads.

Semaphore sa Kotlin para sa Android

Ang Android ay nagbibigay ng class na Semaphore mula sa package na java.util.concurrent. Tingnan natin ang halimbawa ng paglilimita ng sabay na network requests sa dalawang thread upang maiwasan ang server overload.

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

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

DispatchSemaphore sa Swift para sa iOS

Sa iOS, ang DispatchSemaphore mula sa GCD ay nilulutas ang parehong gawain. Ginagamit ito ng mga developer para i-sync ang access sa resources sa asynchronous code nang hindi bina-block ang main thread.

swift
let semaphore = DispatchSemaphore(value: 3)

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

Mga Karaniwang Pagkakamali sa Paggawa gamit ang Semaphores

Ang pinakakaraniwang pagkakamali — nakalimutang release sa exception. Kung ang thread ay natapos na may error bago tumawag ng release, ang semaphore ay mananatiling blocked magpakailanman para sa ibang threads. Gamitin ang try/finally o defer para sa garantisadong pag-release. Ang pangalawang problema ay deadlock kapag kumukuha ng maraming semaphore sa magkaibang pagkakasunod-sunod ng iba't ibang threads.

Mga pattern ng paggamit ng semaphore

Ang semaphores ay ginagamit hindi lamang para sa proteksyon ng data, kundi pati na rin para sa koordinasyon ng threads sa mga kumplikadong multithread na sitwasyon. Ang kaalaman sa mga karaniwang pattern ay nagpapabilis ng development at nagpapababa ng posibilidad ng synchronization errors.

Mayroong ilang mga napatunayang pattern ng paggamit ng semaphore sa mga totoong proyekto. Ang pag-alam sa mga ito ay tumutulong na maiwasan ang mga tipikal na pagkakamali at bumuo ng maaasahang multithread system.

Rate Limiter (paglilimita ng bilis)

Ang semaphore na may initial value N at periodic release sa pamamagitan ng timer ay nagpapatupad ng paglilimita ng bilis ng mga request sa API. Halimbawa, ang serbisyo ay nagpapahintulot ng 10 request bawat segundo: ang semaphore ay nagsisimula sa 10, bawat request ay nagpapababa ng counter, at isang hiwalay na TimerTask ay nagbabalik ng counter sa initial value isang beses bawat segundo. Pinoprotektahan nito ang aplikasyon at server mula sa overload.

Producer-Consumer sa pamamagitan ng semaphores

Sa klasikong producer-consumer task, dalawang semaphore ang namamahala ng buffer: empty (write permissions) at full (read permissions). Ang Producer ay tumatawag ng acquire sa empty at release sa full, ang Consumer — kabaligtaran. Ginagarantiyahan ng scheme na ito na hindi kailanman babasahin ng Consumer ang empty buffer, at hindi ito mapupuno nang sobra ng Producer.

Ang parehong scheme ay nasa puso ng bounded buffer sa operating systems — ang circular buffer na may fixed size. Sa mobile applications, ang pattern ay ginagamit para sa pagproseso ng mga queue ng images, video files, at analytic events.

Throttling ng network requests

Ang semaphores ay matagumpay na ginagamit para sa throttling ng network calls sa background services. Halimbawa, isang analytics application ay nagpapadala ng mga event packet sa server. Kung walang paglilimita ng concurrent threads, sa peak loads (pagsisimula ng application, synchronization pagkatapos ng offline) ang bilang ng sabay na request ay maaaring lumampas sa server limits. Ang semaphore na may initial value na 3 ay ginagarantiyahan ang maayos na pagpapadala at pumipigil sa pag-block sa server side.

Mga Madalas Itanong

Ano ang pagkakaiba ng Semaphore at ordinaryong counter?

Ang semaphore ay hindi lamang isang counter, kundi isang synchronization primitive na may atomic operations at waiting queue. Ang ordinaryong counter ay hindi bina-block ang thread at hindi ginagarantiyahan ang atomicity ng incrementation sa concurrent access ng maraming threads.

Maaari bang maging sanhi ng deadlock ang semaphore?

Oo, ang deadlock ay posible kapag kumukuha ng maraming semaphore sa magkaibang pagkakasunod-sunod ng iba't ibang threads. Halimbawa, thread A ay kumukuha ng S1, pagkatapos S2, at thread B — S2, pagkatapos S1. Magtakda ng uniform na pagkakasunod-sunod ng pagkuha para sa lahat ng semaphore sa proyekto.

Ano ang mangyayari sa acquire na may counter na zero?

Ang thread ay mababa-block at mapupunta sa waiting state. Hindi ito gumagamit ng processor time hanggang sa ibang thread ay tumawag ng release. Sa Java ito ay BLOCKED state, sa Swift ang thread ay sinuspinde ng GCD.

Paano naiiba ang Binary Semaphore sa Mutex sa pangunahing antas?

Ang pangunahing pagkakaiba — pagmamay-ari. Ang Mutex ay maaari lamang i-release ng may-ari na thread. Ang Binary Semaphore ay maaaring i-release ng anumang thread, na maginhawa para sa inter-thread signaling, ngunit hindi gaanong ligtas para sa proteksyon ng data integrity.

Anong initial value ang dapat piliin para sa counter?

Ang initial value ay depende sa sitwasyon. Para sa proteksyon ng isang resource — 1. Para sa pool ng N connections — N. Para sa inter-thread signaling ay gumamit ng 0, upang ang consumer thread ay maghintay ng signal mula sa producer.

Buod

  • Semaphore — synchronization primitive batay sa counter, iminungkahi ni Dijkstra noong 1965.
  • Binary semaphore ay kumukuha ng mga halaga 0 at 1, counting semaphore — anumang non-negative na halaga.
  • Ang acquire at release operations ay atomic at thread-safe.
  • Hindi tulad ng mutex, ang semaphore ay hindi nakatali sa may-ari na thread.
  • Counting semaphores ay ginagamit para sa pamamahala ng resource pools at paglilimita ng parallelism.
  • Nakalimutang release — pinakakaraniwang pagkakamali na humahantong sa pag-freeze ng threads.

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din