Вопрос проверяет понимание конкурентности в iOS, защиты общих данных и выбора правильного примитива синхронизации.
В GCD доступны очереди (serial/concurrent), DispatchSemaphore, DispatchGroup, DispatchWorkItem и barrier. Серийная очередь часто используется как “lock” для защиты состояния. Семофор ограничивает параллелизм и помогает дождаться ресурсов. Барьеры позволяют безопасно писать в общий ресурс на concurrent queue. Выбор зависит от задачи: защита данных, ожидание завершения, ограничение количества операций.
GCD дает несколько способов координировать выполнение кода, и важно понимать, что каждый механизм решает свою проблему.
Самый простой и популярный вариант — защищать состояние через серийную очередь.
Все операции выполняются строго по очереди
Нет одновременного доступа к данным
Удобно как альтернатива lock’ам
Пример:
final class Counter {
private var value = 0
private let queue = DispatchQueue(label: "counter.queue")
func increment() {
queue.sync { value += 1 }
}
func get() -> Int {
queue.sync { value }
}
}
Плюсы: простота, меньше ошибок.
Минусы: может стать узким местом при высокой нагрузке.
DispatchSemaphoreСемафор — это счетчик “разрешений”.
ограничивает количество параллельных задач
может использоваться для ожидания (но аккуратно, чтобы не блокировать main)
Пример ограничения параллелизма:
let semaphore = DispatchSemaphore(value: 4) // максимум 4 параллельно
func runTask(_ block: @escaping () -> Void) {
DispatchQueue.global().async {
semaphore.wait()
block()
semaphore.signal()
}
}
Важно:
wait() блокирует поток
на main thread это почти всегда плохая идея
DispatchGroupГруппа нужна, когда надо дождаться завершения набора задач.
enter/leave для ручного контроля
notify чтобы получить колбэк после всех задач
let group = DispatchGroup()
group.enter()
loadA { group.leave() }
group.enter()
loadB { group.leave() }
group.notify(queue: .main) {
// все завершено
}
Если у вас concurrent queue и общий ресурс, барьер дает “эксклюзивную запись”.
чтения могут выполняться параллельно
запись выполняется строго одна и блокирует остальные блоки
let queue = DispatchQueue(label: "store.queue", attributes: .concurrent)
var store: [String: Int] = [:]
func read(_ key: String) -> Int? {
queue.sync { store[key] }
}
func write(_ key: String, _ value: Int) {
queue.async(flags: .barrier) {
store[key] = value
}
}
DispatchWorkItem и отменаWorkItem позволяет:
отменять выполнение (кооперативно)
задавать зависимости и повторное использование блока
Но отмена не “убивает” поток, она лишь сигнализирует, что работу можно не продолжать.
В GCD есть несколько уровней синхронизации: serial queue для защиты состояния, semaphore для ограничения параллелизма и ожидания ресурсов, group для ожидания набора задач, barrier для безопасной записи на concurrent queue, work item для управления и отмены. Правильный выбор примитива — это половина успеха в конкурентном коде: он делает систему предсказуемой и снижает риск гонок данных и дедлоков.