← Назад к вопросам

Как добавить Semaphore в программу?

1.7 Middle🔥 191 комментариев
#Многопоточность и асинхронность

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Использование 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
    }
}

Важные нюансы использования

  1. Дедлоки (взаимные блокировки)

    // ОШИБКА: может привести к дедлоку
    semaphore.wait()
    someCriticalOperation()
    semaphore.wait() // Блокировка здесь!
    semaphore.signal()
    semaphore.signal()
    
  2. Таймауты для избежания вечного ожидания

    let semaphore = DispatchSemaphore(value: 0)
    
    DispatchQueue.global().async {
        if semaphore.wait(timeout: .now() + 5) == .timedOut {
            print("Таймаут ожидания семафора")
            return
        }
        // Продолжение работы
    }
    
  3. Инициализация со значением ресурсов

    // Правильно: значение соответствует количеству доступных ресурсов
    let databaseConnections = DispatchSemaphore(value: 5)
    
    // Неправильно: отрицательные значения могут вызвать неопределённое поведение
    let invalidSemaphore = DispatchSemaphore(value: -1)
    

Альтернативы семафорам в Swift

  1. OperationQueue с maxConcurrentOperationCount

    let operationQueue = OperationQueue()
    operationQueue.maxConcurrentOperationCount = 3
    
  2. Actor (Swift 5.5+)

    actor ResourceManager {
        private var connections = 0
        
        func acquireConnection() async {
            // Автоматическая синхронизация через actor
            connections += 1
        }
    }
    

Рекомендации по использованию

  • Всегда используйте defer для гарантированного освобождения семафора
  • Избегайте длительных операций в критических секциях
  • Тестируйте edge cases: таймауты, отмены операций, исключения
  • Предпочитайте высокоуровневые API (DispatchGroup, OperationQueue) когда это возможно

Семафоры предоставляют низкоуровневый, но мощный механизм синхронизации, который особенно полезен при работе с ограниченными ресурсами, контроле потока выполнения и реализации сложных паттернов межпоточного взаимодействия.

Как добавить Semaphore в программу? | PrepBro