> В чем разница между семафором и барьером? (iOS, Swift)

Уровень: senior · Роль: mobile · Категория: Технические вопросы

Компании: MTS

Стек: iOS, Swift

> Пример ответа

Короткий ответ

Семафор ограничивает количество одновременных доступов к ресурсу, а барьер синхронизирует группу потоков, заставляя их ждать друг друга в определённой точке. Семафор - это счётчик, который можно увеличивать и уменьшать, барьер - это точка встречи, после которой все потоки продолжают работу вместе. В iOS семафор реализован через DispatchSemaphore, барьер - через DispatchGroup или DispatchWorkItem с флагом .barrier.

Подробное объяснение

Семафор и барьер решают разные задачи синхронизации, хотя оба относятся к примитивам конкурентности.

Семафор - это счётчик с двумя операциями: wait (уменьшает счётчик, блокирует поток если счётчик равен нулю) и signal (увеличивает счётчик, разблокирует ожидающий поток). Основное назначение - ограничение параллелизма: например, разрешить только 3 одновременных сетевых запроса или 2 операции записи в файл. Семафор не привязан к конкретным потокам - любой поток может вызвать signal, даже если не вызывал wait.

Барьер - это механизм, который заставляет все потоки из группы дождаться друг друга в определённой точке. Когда поток достигает барьера, он блокируется, пока все остальные потоки группы не достигнут того же барьера. После этого все потоки одновременно продолжают выполнение. В GCD барьер реализуется через DispatchGroup с enter/leave и wait, или через DispatchQueue с атрибутом .concurrent и флагом .barrier для отдельных задач.

Ключевое различие: семафор не знает, какие именно потоки его используют - он просто считает. Барьер знает состав группы и ждёт конкретных участников. Семафор можно использовать повторно неограниченное количество раз, барьер обычно одноразовый (хотя DispatchGroup можно переиспользовать).

На практике

В мобильной разработке семафор чаще применяется для ограничения параллелизма: лимит одновременных загрузок изображений, защита пула соединений, ограничение количества фоновых задач. Барьер - для координации: дождаться завершения нескольких параллельных операций перед обновлением UI, синхронизировать загрузку данных из разных источников перед отображением.

В iOS семафор удобен для ожидания результата асинхронной операции в синхронном контексте (например, в тестах). Барьер через DispatchGroup - стандартный способ дождаться группы задач. Важно помнить: семафор с счётчиком 1 - это фактически мьютекс, но использовать его так не рекомендуется из-за отсутствия владения потоком.

Пример кода

SWIFT
// Семафор: ограничение параллелизма до 2 операций
let semaphore = DispatchSemaphore(value: 2)
let queue = DispatchQueue.global()
for i in 0..<5 {
queue.async {
semaphore.wait()
print("Старт операции \(i)")
Thread.sleep(forTimeInterval: 1)
print("Финиш операции \(i)")
semaphore.signal()
}
}
// Барьер через DispatchGroup: ждём все задачи
let group = DispatchGroup()
let groupQueue = DispatchQueue.global()
for i in 0..<3 {
group.enter()
groupQueue.async {
print("Задача \(i) началась")
Thread.sleep(forTimeInterval: 1)
print("Задача \(i) завершилась")
group.leave()
}
}
group.notify(queue: .main) {
print("Все задачи завершены, обновляем UI")
}

Как отвечать на собеседовании

Начните с чёткого противопоставления: семафор - про ограничение количества, барьер - про синхронизацию группы. Приведите конкретный пример из iOS-разработки для каждого. Упомяните, что семафор не привязан к потокам, а барьер - привязан к группе. Если спросят про DispatchQueue с .barrier - объясните, что это частный случай барьера для очереди, где задачи до барьера и после выполняются конкурентно, но барьерная задача - эксклюзивно.

Хорошо показать понимание trade-off: семафор проще и гибче, но легко забыть вызвать signal - будет deadlock. Барьер безопаснее для групповой синхронизации, но требует явного управления enter/leave.

Что проверяет интервьюер

Интервьюер проверяет: понимание базовых примитивов синхронизации, умение выбрать правильный инструмент под задачу, знание GCD API в iOS, осознание различий между ограничением параллелизма и координацией потоков. Также оценивается способность объяснить абстрактные концепции на конкретных примерах и предвидеть проблемы (deadlock, race condition).

Типичные ошибки

  • Путаница семафора с мьютексом: семафор со значением 1 - это не мьютекс, у него нет владельца.
  • Утверждение, что барьер останавливает все потоки системы - барьер работает только с потоками своей группы.
  • Забывают, что DispatchSemaphore блокирует поток, на котором вызван wait - это может привести к проблемам в main queue.
  • Неправильное использование DispatchGroup: вызов leave без enter вызывает crash.
  • Ожидание, что барьер в DispatchQueue с .barrier работает как полная синхронизация всех задач - на самом деле он синхронизирует только задачи этой очереди.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью