Ответ
Семафор — это механизм синхронизации, который контролирует доступ ограниченного числа потоков к общему ресурсу или критической секции кода. В 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).
Ответ 18+ 🔞
А, семафоры, ёпта! Ну это ж классика, как "Муму" Тургенева, только в мире многопоточности. Сейчас объясню, но без вот этого вот "Му-му", блядь.
Представь себе, сука, туалет в офисе. Один унитаз, а программистов — овердохуища. Все хотят посрать, но дверь-то одна, блядь. Вот семафор — это как сантехник с ключом, который пускает по одному. Или, если кабинок несколько, то по два, по три. Внутри у него счётчик, как табло свободных мест.
Как это работает, блядь: У сантехника (семафора) есть цифра. Ты подходишь, говоришь "wait()" — он цифру уменьшает. Если цифра ушла в минус — всё, браток, стой и жди, пока кто-то не выйдет и не крикнет "signal()". Тогда цифра подрастёт, и тебя пустят.
Основные команды, которые он понимает:
DispatchSemaphore(value: 3)— "Так, на три толчка рассчитано, блядь!" (инициализация).wait()— "Можно я?" (занимает слот, ждёт, если нет).signal()— "Я всё, выхожу!" (освобождает слот).
Пример: Не дать серверу сдохнуть от запросов Допустим, у тебя 100 URL'ов, но API твоего бэкенда — хилая жопа, и больше трёх одновременных запросов не вывозит. Делаем так:
// Сантехник на три кабинки
let semaphore = DispatchSemaphore(value: 3)
for url in urls {
DispatchQueue.global().async {
semaphore.wait() // Ждём своей очереди в сортир
makeNetworkRequest(to: url) {
// Сделал дело — гуляй смело
semaphore.signal() // Освобождаем кабинку
}
}
}
Вот и всё, сервер не ляжет, как опьяневший Герасим, потому что больше трёх потоков в него не полезут одновременно.
Пример: Чтобы не наступить друг другу на мозги в общей памяти Есть массив, и 100 потоков хотят в него что-то пихнуть. Если пустить всех — будет пиздец, данные перемешаются в кашу. Делаем семафор-мьютекс (на одну кабинку):
let semaphore = DispatchSemaphore(value: 1) // Один унитаз на всех!
var sharedArray = [Int]()
DispatchQueue.concurrentPerform(iterations: 100) { i in
semaphore.wait() // Закрылся в сортире
// Начало критической секции (можно спокойно срать)
sharedArray.append(i)
// Конец критической секции
semaphore.signal() // Открыл дверь
}
Теперь массив будет целым, а не как после пьянки.
Где это, блядь, применять:
- Чтобы не заDDOS'ить ничего (сеть, диск, базу).
- Когда есть общая переменная, которую все портят.
- Для всяких хитрожопых паттернов вроде "читателей-писателей".
⚠️ А теперь, сука, внимание, главное!
- Взаимная блокировка (deadlock): Следи, чтобы
wait()иsignal()всегда были парными, как носки. Если забудешьsignal()— всё, твоя программа встала колом, все потоки упёрлись в закрытую дверь и ждут, а выйти некому. Пиздец и тишина. - Не дрочи лишнего: Не используй семафоры везде, где не надо. Для простой защиты свойства часто хватает обычной очереди с барьером (
DispatchQueue.barrier) или, на новых iOS, акторов. Не усложняй, блядь, жизнь без нужды.
Вот и вся философия. Проще, чем Тургенева понять, а пользы — больше. Главное — не заблокировать всех нахуй по ошибке.