Семафор (Semaphore) — это механизм синхронизации, который контролирует доступ к общему ресурсу с помощью счётчика. В отличие от mutex, семафор может разрешать доступ нескольким потокам одновременно (в зависимости от заданного значения счётчика).
wait()
: Уменьшает счётчик (захватывает ресурс)signal()
: Увеличивает счётчик (освобождает ресурс)Двоичный семафор (Binary Semaphore):
Счётный семафор (Counting Semaphore):
import Foundation
// Создаём семафор с начальным значением 2
let semaphore = DispatchSemaphore(value: 2)
func performTask(id: Int) {
print("Задача \(id) ждёт")
semaphore.wait() // Уменьшаем счётчик
defer {
semaphore.signal() // Всегда освобождаем
print("Задача \(id) завершена")
}
print("Задача \(id) выполняется")
Thread.sleep(forTimeInterval: 1) // Имитация работы
}
for i in 1...5 {
DispatchQueue.global().async {
performTask(id: i)
}
}
Вывод программы покажет, что одновременно выполняются только 2 задачи.
Характеристика | Семафор | Mutex |
---|---|---|
Владелец | Нет понятия владельца | Запоминает владельца |
Количество потоков | Может пропускать несколько | Только один поток |
Освобождение | Любой поток может signal | Только владеющий поток |
Использование | Ограничение параллелизма | Защита shared-ресурсов |
Ограничение одновременных сетевых запросов:
let networkSemaphore = DispatchSemaphore(value: 3) // Макс 3 запроса
Синхронизация между асинхронными задачами:
let semaphore = DispatchSemaphore(value: 0)
someAsyncOperation {
// Завершение операции
semaphore.signal()
}
// Ожидание завершения
semaphore.wait()
Реализация producer-consumer:
let itemsSemaphore = DispatchSemaphore(value: 0)
let spaceSemaphore = DispatchSemaphore(value: bufferSize)
if semaphore.wait(timeout: .now() + 5) == .timedOut {
print("Таймаут ожидания")
}
Async/Await с TaskGroup:
await withTaskGroup(of: Void.self) { group in
for _ in 1...5 {
group.addTask { await processItem() }
}
}
OperationQueue с maxConcurrentOperationCount:
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 2
семафоры — мощный инструмент для управления доступом к ресурсам и синхронизации потоков. В отличие от mutex, они позволяют контролировать количество одновременных доступов. В iOS чаще всего используют DispatchSemaphore, но в новых проектах предпочтительнее применять современные механизмы Swift Concurrency.