Semaphore: шта је, принцип рада и примена у синхронизацији нити

Аутор: IT Sectr Објављено: 2026-03-19 Време читања: 8 мин

Semaphore је примитив синхронизације који управља приступом заједничком ресурсу преко бројача и реда нити које чекају. Према Wikipedia, 2024, семафор је предложио Едсгер Дајкстра 1965. године за решавање проблема вишенитне интеракције. Алат омогућава ограничавање броја нити које истовремено раде са критичном секцијом.

Главно

  • Semaphore — примитив синхронизације који управља приступом преко бројача дозвола.
  • Бинарни семафор узима вредности 0 и 1, радећи као заставица блокирања.
  • Бројачки семафор допушта истовремени приступ одређеном броју нити.
  • За разлику од мутекса, семафор није везан за нит-власника.
  • Deadlock — једна од главних опасности при неправилној употреби семафора.

Шта је Semaphore?

Semaphore — је примитив синхронизације који користи бројач за управљање приступом заједничком ресурсу. Концепт је предложио Едсгер Дајкстра 1965. године и постао је темељ свих модерних механизама синхронизације у оперативним системима.

Дефиниција и намена

Семафор представља целобројну променљиву са две атомске операције: wait (acquire) и signal (release). Операција wait смањује бројач, а signal га повећава. Када бројач достигне нулу, нит која позива wait се блокира док signal не изврши друга нит.

Основна намена семафора је заштита критичних секција од истовременог приступа више нити. За разлику од мутекса, семафор не захтева везивање за нит-власника, што га чини погодним за шири круг задатака координације.

Историја и теоријска основа

Концепт семафора настао је у контексту оперативног система THE, развијеног на Technische Hogeschool Eindhoven. Дајкстра је формализовао семафор као математичку апстракцију, доказавши његову довољност за имплементацију било којих примитива синхронизације.

Како ради семафор?

Механизам семафора се заснива на две атомске операције и унутрашњем реду чекања. При позиву acquire нит проверава вредност бројача и или наставља извршење или се блокира до ослобађања ресурса.

Бројач и атомске операције

При стварању семафора поставља се почетна вредност бројача дозвола. Сваки позив acquire смањује бројач за 1. Ако након тога бројач постане негативан, нит се блокира. Операција release повећава бројач и буди једну од нити које чекају.

kotlin
import java.util.concurrent.Semaphore

val semaphore = Semaphore(3)

fun accessResource() {
    semaphore.acquire()
    try {
        println("${Thread.currentThread().name} ради")
    } finally {
        semaphore.release()
    }
}

Ред чекања и планирање

Када нит позове acquire при нултом бројачу, ОС је ставља у FIFO ред семафора. Нит прелази у стање BLOCKED, не трошећи процесорско време. Након позива release, прва нит у реду прелази у стање RUNNABLE и добија приступ ресурсу.

Типови семафора

У теорији синхронизације издвајају се два главна типа семафора: бинарни (binary) и бројачки (counting). Избор типа зависи од конкретног задатка управљања приступом ресурсима.

Бинарни семафор (Binary Semaphore)

Бинарни семафор узима само вредности 0 и 1. По понашању подсећа на мутекс, али без захтева власништва — било која нит може извршити release. Такви семафори су погодни за имплементацију заставица спремности и догађаја између нити.

kotlin
val ready = Semaphore(0)

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

fun consumer() {
    ready.acquire()
    println("Подаци су спремни")
}

Бројачки семафор (Counting Semaphore)

Бројачки семафор може узети било коју ненегативну вредност. Користи се за управљање базеном истоврсних ресурса где је доступно неколико инстанци. На пример, базен од 5 мрежних веза: сваки acquire заузима једну везу, release је враћа у базен.

Бројачки семафори су незаменљиви при ограничавању брзине приступа спољним сервисима и имплементацији базена нити. Они омогућавају прецизну контролу степена паралелизма без ручног управљања нитима.

ПараметарБинарни семафорБројачки семафор
Опсег0 или 1од 0 до N
Нити истовремено1до N
Применасигнализација, заставицебазени ресурса, rate limiting

Semaphore vs Мутекс

Програмери често мешају семафор и мутекс, иако постоје фундаменталне разлике између њих. Разумевање ових разлика је кључно за избор исправног механизма синхронизације у пројекту.

Принцип власништва

Кључна разлика — концепт власништва. Мутекс увек зна која нит га је заузела и само та нит га може ослободити. Семафор нема власника: било која нит може позвати release, чак и без позива acquire. То чини мутекс сигурнијим за заштиту података, а семафор — флексибилнијим за координацију.

Перформансе и сценарији употребе

У пракси, мутекс је бржи за једноставно међусобно блокирање захваљујући оптимизацијама за типичан сценарио. Семафор захтева додатне трошкове за одржавање бројача. Међутим, за ограничавање паралелизма или имплементацију обрасца „произвођач-потрошач“, семафор је незаменљив.

КарактеристикаSemaphoreМутекс
Власништвонема власникаима власника
Ослобађањебило која нитсамо нит-власник
Бројачод 0 до Nбинаран
Употребаограничавање паралелизма и сигнализацијазаштита критичне секције
Рекурзијанеда (reentrant)

Примена Semaphore у мобилном развоју

У развоју мобилних апликација Semaphore се користи за управљање приступом ограниченим ресурсима: мрежним везама, датотекама, базама података и хардверским компонентама. Савремене платформе пружају згодне уграђене имплементације.

Базен веза са сервером

Један од типичних случајева — базен HTTP веза. Апликација може истовремено слати највише 4 захтева ка серверу, јер API провајдера ограничава паралелизам. Семафор са почетном вредношћу 4 гарантује да при било ком оптерећењу број истовремених захтева неће прећи границу, а остале нити ће чекати у реду.

Без семафора, при наглом повећању активности корисника, серверска инфраструктура може доживети изненадно преоптерећење, што доводи до тајм-аута и грешака 429 Too Many Requests. Семафор ради као осигурач, пропуштајући строго одређен број истовремених позива без обзира на број активних нити.

Semaphore у Kotlin за Android

Android пружа класу Semaphore из пакета java.util.concurrent. Размотримо пример ограничавања истовремених мрежних захтева на две нити ради спречавања преоптерећења сервера.

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

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

DispatchSemaphore у Swift за iOS

У iOS-у, DispatchSemaphore из GCD решава исти задатак. Програмери га користе за синхронизацију приступа ресурсима у асинхроном коду без блокирања главне нити.

swift
let semaphore = DispatchSemaphore(value: 3)

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

Типичне грешке при раду са семафорима

Најчешћа грешка — заборављени release при изузетку. Ако нит заврши са грешком пре позива release, семафор заувек остаје блокиран за остале нити. Користите try/finally или defer за гарантовано ослобађање. Други проблем — deadlock при заузимању више семафора у различитом редоследу од различитих нити.

Обрасци употребе семафора

Семафори се примењују не само за заштиту података, већ и за координацију нити у сложеним вишенитним сценаријима. Познавање уобичајених образаца убрзава развој и смањује вероватноћу грешака синхронизације.

Постоји неколико проверених образаца примене семафора у реалним пројектима. Њихово познавање помаже у избегавању типичних грешака и изградњи поузданих вишенитних система.

Rate Limiter (ограничавање брзине)

Семафор са почетном вредношћу N и периодичним release путем тајмера имплементира ограничавање брзине захтева ка API-ју. На пример, сервис дозвољава 10 захтева у секунди: семафор стартује са 10, сваки захтев смањује бројач, а засебан TimerTask једном у секунди враћа бројач на почетну вредност. Ово штити и апликацију и сервер од преоптерећења.

Произвођач-Потрошач преко семафора

У класичном задатку произвођач-потрошач два семафора управљају бафером: empty (дозволе за писање) и full (дозволе за читање). Произвођач позива acquire на empty и release на full, Потрошач — обрнуто. Ова шема гарантује да Потрошач никада неће прочитати празан бафер, а Произвођач га неће препунити.

Иста шема лежи у основи ограниченог бафера у оперативним системима — кружног бафера фиксне величине. У мобилним апликацијама образац се користи за обраду редова слика, видео датотека и аналитичких догађаја.

Throttling мрежних захтева

Семафори се успешно користе за throttling мрежних позива у позадинским сервисима. На пример, апликација за аналитику шаље пакете догађаја на сервер. Без ограничавања конкурентних нити при вршним оптерећењима (покретање апликације, синхронизација након офлајна) број истовремених захтева може премашити границе сервера. Семафор са почетном вредношћу 3 гарантује глатко слање и спречава блокирање на страни сервера.

Често постављана питања

Која је разлика између Semaphore и обичног бројача?

Семафор није обичан бројач, већ примитив синхронизације са атомским операцијама и редом чекања. Обичан бројач не блокира нит и не гарантује атомскост инкремента при конкурентном приступу више нити.

Може ли семафор постати узрок deadlock-а?

Да, deadlock је могућ при заузимању више семафора у различитом редоследу од различитих нити. На пример, нит A заузима S1, затим S2, а нит B — S2, затим S1. Утврдите јединствени редослед заузимања за све семафоре у пројекту.

Шта се дешава при acquire са нултим бројачем?

Нит се блокира и прелази у стање чекања. Не троши процесорско време док друга нит не позове release. У Java-и је ово стање BLOCKED, у Swift-у нит се зауставља од стране GCD.

Чим се Binary Semaphore суштински разликује од Mutex-а?

Основна разлика — власништво. Mutex може ослободити само нит-власник. Binary Semaphore може ослободити било која нит, што је згодно за сигнализацију између нити, али мање безбедно за заштиту интегритета података.

Коју почетну вредност изабрати за бројач?

Почетна вредност зависи од сценарија. За заштиту једног ресурса — 1. За базен од N веза — N. За сигнализацију између нити користите 0, тако да нит-потрошач чека сигнал од произвођача.

Закључци

  • Semaphore — примитив синхронизације заснован на бројачу, који је предложио Дајкстра 1965. године.
  • Бинарни семафор узима вредности 0 и 1, бројачки — било коју ненегативну вредност.
  • Операције acquire и release су атомске и нитно безбедне.
  • За разлику од мутекса, семафор није везан за нит-власника.
  • Бројачки семафори се користе за управљање базенима ресурса и ограничавање паралелизма.
  • Заборављени release — најчешћа грешка која доводи до замрзавања нити.

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође