Ответ
Семафор — это механизм синхронизации, который контролирует доступ ограниченного числа потоков к общему ресурсу или критической секции кода. В iOS представлен классом DispatchSemaphore из Grand Central Dispatch (GCD).
Принцип работы:
Семафор содержит внутренний счетчик. Поток, вызывающий wait(), уменьшает счетчик. Если счетчик становится отрицательным, поток блокируется до тех пор, пока другой поток не вызовет signal(), увеличив счетчик.
Основные методы:
DispatchSemaphore(value: Int)— инициализация с начальным значением счетчика.signal()— увеличивает счетчик на 1.wait()— уменьшает счетчик на 1; блокирует поток, если счетчик < 0.wait(timeout:)— ожидание с таймаутом.
Пример: Ограничение одновременных сетевых запросов
// Ограничиваем 3 одновременных запроса
let semaphore = DispatchSemaphore(value: 3)
for url in urls {
DispatchQueue.global().async {
semaphore.wait() // Занимаем слот
makeNetworkRequest(to: url) {
semaphore.signal() // Освобождаем слот
}
}
}
Пример: Защита критической секции (аналог мьютекса)
let semaphore = DispatchSemaphore(value: 1) // Только один поток
var sharedArray = [Int]()
DispatchQueue.concurrentPerform(iterations: 100) { i in
semaphore.wait()
// Начало критической секции
sharedArray.append(i)
// Конец критической секции
semaphore.signal()
}
Типичные сценарии использования:
- Ограничение количества одновременных операций (например, сетевых запросов, операций ввода-вывода).
- Синхронизация доступа к общим, изменяемым ресурсам.
- Реализация паттернов, таких как "читатели-писатели".
⚠️ Важные замечания:
- Deadlock: Убедитесь, что
wait()иsignal()вызываются парно и в правильном порядке, особенно в сложных или условных ветвлениях кода. - Производительность: Используйте семафоры только там, где это необходимо. Для простой синхронизации доступа к свойствам часто достаточно
DispatchQueueс барьерами (barrier) или акторов (Actor).