Как добавить Semaphore в программу?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Использование Semaphore в программировании
Семафор — это примитив синхронизации, который позволяет контролировать доступ к общему ресурсу несколькими потоками (или процессами) через счётчик и механизм ожидания. Основные операции: wait() (захват) и signal() (освобождение).
Основные типы семафоров
1. Бинарный семафор
Работает как мьютекс (значение 0 или 1), но может освобождаться другим потоком.
let binarySemaphore = DispatchSemaphore(value: 1)
binarySemaphore.wait()
// Критическая секция
binarySemaphore.signal()
2. Счётный семафор
Позволяет задавать начальное значение N, ограничивая одновременный доступ N потоками.
let concurrentSemaphore = DispatchSemaphore(value: 3)
func accessResource() {
concurrentSemaphore.wait()
// Работа с ресурсом (максимум 3 потока одновременно)
concurrentSemaphore.signal()
}
Практическое применение в iOS/macOS
Пример 1: Ограничение одновременных сетевых запросов
class NetworkService {
private let requestSemaphore = DispatchSemaphore(value: 2)
private let queue = DispatchQueue(label: "com.example.network", attributes: .concurrent)
func makeConcurrentRequests(urls: [URL]) {
urls.forEach { url in
queue.async {
self.requestSemaphore.wait()
// Выполнение запроса
URLSession.shared.dataTask(with: url) { data, response, error in
// Обработка ответа
self.requestSemaphore.signal()
}.resume()
}
}
}
}
Пример 2: Синхронизация работы с разделяемым ресурсом
class ThreadSafeArray<T> {
private var array = [T]()
private let semaphore = DispatchSemaphore(value: 1)
func append(_ element: T) {
semaphore.wait()
defer { semaphore.signal() }
array.append(element)
}
var count: Int {
semaphore.wait()
defer { semaphore.signal() }
return array.count
}
}
Важные нюансы использования
-
Дедлоки (взаимные блокировки)
// ОШИБКА: может привести к дедлоку semaphore.wait() someCriticalOperation() semaphore.wait() // Блокировка здесь! semaphore.signal() semaphore.signal() -
Таймауты для избежания вечного ожидания
let semaphore = DispatchSemaphore(value: 0) DispatchQueue.global().async { if semaphore.wait(timeout: .now() + 5) == .timedOut { print("Таймаут ожидания семафора") return } // Продолжение работы } -
Инициализация со значением ресурсов
// Правильно: значение соответствует количеству доступных ресурсов let databaseConnections = DispatchSemaphore(value: 5) // Неправильно: отрицательные значения могут вызвать неопределённое поведение let invalidSemaphore = DispatchSemaphore(value: -1)
Альтернативы семафорам в Swift
-
OperationQueue с maxConcurrentOperationCount
let operationQueue = OperationQueue() operationQueue.maxConcurrentOperationCount = 3 -
Actor (Swift 5.5+)
actor ResourceManager { private var connections = 0 func acquireConnection() async { // Автоматическая синхронизация через actor connections += 1 } }
Рекомендации по использованию
- Всегда используйте
deferдля гарантированного освобождения семафора - Избегайте длительных операций в критических секциях
- Тестируйте edge cases: таймауты, отмены операций, исключения
- Предпочитайте высокоуровневые API (DispatchGroup, OperationQueue) когда это возможно
Семафоры предоставляют низкоуровневый, но мощный механизм синхронизации, который особенно полезен при работе с ограниченными ресурсами, контроле потока выполнения и реализации сложных паттернов межпоточного взаимодействия.