Ответ
Semaphore (DispatchSemaphore) используется для ограничения количества одновременных операций, обращающихся к общему ресурсу с ограниченной ёмкостью.
Типичные сценарии:
- Rate limiting API-запросов — чтобы не превысить лимиты сервера.
- Ограничение параллельных файловых операций или сетевых соединений.
- Синхронизация доступа к ресурсу, когда
DispatchQueueилиOperationQueueнедостаточно гибки.
Пример (Swift):
// Ограничиваем максимум 3 одновременные задачи
let semaphore = DispatchSemaphore(value: 3)
for i in 1...10 {
DispatchQueue.global().async {
semaphore.wait() // Запрашиваем доступ
defer { semaphore.signal() } // Гарантированно освобождаем
// Критическая секция: работа с ограниченным ресурсом
print("Задача (i) выполняется")
Thread.sleep(forTimeInterval: 1)
}
}
Важные предостережения:
- Всегда используйте
defer { semaphore.signal() }или аналогичный механизм, чтобы избежать deadlock. - Никогда не вызывайте
wait()на главном потоке — это заблокирует UI. - Для большинства задач синхронизации предпочтительнее серийные очереди (
DispatchQueue) илиOperationQueueс установленнымmaxConcurrentOperationCount.
Ответ 18+ 🔞
А, ну вот, семафоры, блядь! Ну это ж классика, ёпта! Стоит только заикнуться про ограничение потоков — все сразу такие: «О, семафор!». Ну, типа, умные.
Смотри, в чём суть, если на пальцах. Представь себе туалет в каком-нибудь офисе, где кабинок всего, допустим, три штуки. Вот это и есть твой семафор, блядь, с value: 3. Значение — это количество свободных кабинок, проще говоря, мест.
И вот приходит народ, десять человек, и каждый хочет посрать. Первые трое заходят — всё ок. Четвёртый подходит, дергает ручку — а там занято, блядь. И стоит он, ждёт, пока кто-то не выйдет. Вот этот момент, когда он ждёт у двери — это и есть вызов semaphore.wait(). Пока не освободится место — не пускает.
А как чувак вышел из кабинки, он как бы говорит: «Я всё, свободно!». Это вызов semaphore.signal(). И наш четвёртый, который ждал, тут же заскакивает внутрь.
Вот тебе код, который эту драму и изображает. Смотри, не спутай:
// Ограничиваем максимум 3 одновременные задачи
let semaphore = DispatchSemaphore(value: 3)
for i in 1...10 {
DispatchQueue.global().async {
semaphore.wait() // Стоим в очереди в туалет, блядь
defer { semaphore.signal() } // Выйдя, крикнем "СВОБОДНО!", даже если нас посреди дела вырвет
// Критическая секция: работа с ограниченным ресурсом
print("Задача (i) выполняется")
Thread.sleep(forTimeInterval: 1)
}
}
Видишь defer? Это, сука, гениальная штука. Она говорит: «Что бы ни случилось дальше в этой функции — вылет, ошибка, инопланетяне прилетели — когда мы будем из неё выходить, выполни semaphore.signal()». Это чтобы не получилось, что чувак в кабинке умер и не вышел, а все остальные стоят и ждут вечность. Deadlock, короче. Страшная хуйня.
А теперь, внимание, три главных правила, чтобы не обосраться:
defer { semaphore.signal() }— твой лучший друг. Запомни это как «Отче наш». Захотел в туалет (wait) — сразу планируй, как объявишь о выходе (signal). Иначе застрянешь там навсегда, и все будут на тебя ругаться.- НИКОГДА, блядь, НИКОГДА не вызывай
wait()на главном потоке. Это как встать в очередь в единственный туалет и заблокировать дверь изнутри, пока ждёшь. Весь интерфейс зависнет, пользователь охуеет. Делай это только в фоновых потоках. - Не умничай раньше времени. В 90% случаев, когда тебе кажется, что нужен семафор, на самом деле хватит обычной серийной очереди (
DispatchQueue) илиOperationQueue, где можно просто выставитьmaxConcurrentOperationCount. Семафор — это как кувалда, когда нужен молоток. Работает? Да. Изящно? Нет. Оверкилл, блядь.
Вот и вся магия. Вошёл — вышел. Сигнал дал. Главное — не забудь спустить воду.