> Какие механизмы синхронизации существуют для решения проблем многопоточности (iOS, Swift)
Уровень: senior · Роль: mobile · Категория: Технические вопросы
Компании: Цифровые решения
Стек: iOS, Swift
> Пример ответа
Короткий ответ
В iOS-разработке основные механизмы синхронизации: NSLock, NSRecursiveLock, NSCondition, NSConditionLock, NSDistributedLock, @synchronized, семафоры (DispatchSemaphore), группы (DispatchGroup), барьеры (DispatchWorkItemFlags.barrier), очереди с последовательным выполнением (DispatchQueue с qos), а также высокоуровневые абстракции: OperationQueue с maxConcurrentOperationCount, Actor (Swift 5.5+) и @MainActor. Выбор зависит от сценария: защита критической секции, координация задач или ограничение конкурентного доступа.
Подробное объяснение
Все механизмы решают три базовые проблемы: гонка данных (data race), взаимная блокировка (deadlock) и инверсия приоритетов. В iOS-контексте важно разделять низкоуровневые примитивы и высокоуровневые абстракции.
Низкоуровневые примитивы:
NSLock- нерекурсивная блокировка. Повторный захват тем же потоком вызывает deadlock.NSRecursiveLock- позволяет повторный захват тем же потоком, но медленнее.NSCondition- блокировка + условная переменная. Позволяет потоку ждать выполнения условия.NSConditionLock- блокировка с целочисленным состоянием, удобна для пошаговых алгоритмов.@synchronized- Objective-C конструкция, работает через ассоциированные объекты. Удобна, но имеет скрытые накладные расходы.
GCD-механизмы:
DispatchSemaphore- счётчик для ограничения количества одновременных доступов.wait()блокирует,signal()освобождает.DispatchGroup- ожидание завершения группы задач.notify()для асинхронного продолжения.DispatchWorkItemFlags.barrier- барьер в конкурентной очереди: задачи до барьера выполняются конкурентно, после - последовательно.- Последовательная очередь (
DispatchQueueсattributes: []) - простейший способ сериализовать доступ к ресурсу.
Высокоуровневые абстракции:
OperationQueueсmaxConcurrentOperationCount = 1- аналог последовательной очереди с поддержкой зависимостей и отмены.Actor- изолирует состояние внутри себя, компилятор гарантирует безопасность доступа.@MainActor- для UI-потока.NSLock+NSCondition- исторически используются в legacy-коде.
Ключевые trade-off:
- Блокировки проще, но подвержены deadlock и имеют накладные расходы на захват/освобождение.
- GCD-механизмы более декларативны, но сложнее в отладке.
- Actors - самый безопасный вариант, но требуют Swift 5.5+ и понимания async/await.
На практике
Для защиты свойства в классе чаще используют последовательную очередь или actor, а не NSLock, потому что это снижает риск ошибок. Для ограничения количества одновременных сетевых запросов - DispatchSemaphore. Для ожидания нескольких параллельных загрузок - DispatchGroup. Для синхронизации чтения/записи (много читателей, один писатель) - барьер в конкурентной очереди.
Пример из реального кода: кэш изображений. Чтение частое, запись редкая. Используем конкурентную очередь с барьером для записи и обычный доступ для чтения. Это даёт производительность без гонок.
Пример кода
SWIFTfinal class ImageCache {private var storage: [String: UIImage] = [:]private let queue = DispatchQueue(label: "image.cache", attributes: .concurrent)func image(forKey key: String) -> UIImage? {queue.sync {storage[key]}}func setImage(_ image: UIImage, forKey key: String) {queue.async(flags: .barrier) {self.storage[key] = image}}}
Пример с actor:
SWIFTactor Counter {private var value = 0func increment() {value += 1}func getValue() -> Int {value}}
Как отвечать на собеседовании
Начни с классификации: примитивы блокировок, GCD-механизмы, высокоуровневые абстракции. Затем объясни, когда что выбирать. Обязательно упомяни проблемы: deadlock, priority inversion, race condition. Приведи пример из практики - кэш или пул ресурсов. Если спросят про производительность, сравни NSLock и os_unfair_lock (последний быстрее, но не рекурсивный). Для senior-уровня важно показать понимание, что синхронизация - это не только блокировки, но и проектирование: immutable-структуры, value types, избегание общего состояния.
Что проверяет интервьюер
- Понимание различий между механизмами и их ограничений.
- Умение выбирать инструмент под конкретный сценарий.
- Знание типичных ошибок: deadlock при
NSLock, использование@synchronizedв hot path, неправильныйDispatchSemaphore(забылиsignal). - Понимание модели памяти Swift и гарантий actor.
- Способность объяснить trade-off между производительностью и безопасностью.
Типичные ошибки
- Использование
NSLockбезdefer { lock.unlock() }- при исключении блокировка остаётся захваченной. - Путаница между
DispatchSemaphoreиDispatchGroup: семафор - для ограничения, группа - для ожидания завершения. - Применение
@synchronizedв Swift-коде - лучше использоватьNSLockили actor. - Игнорирование
os_unfair_lockкак более быстрой альтернативыNSLockдля простых случаев. - Создание deadlock при использовании нескольких блокировок в разном порядке.
- Предположение, что
DispatchQueueсattributes: .concurrentбезопасен для записи без барьера.
> Похожие задачи по mobile
Как работает дедлок и как его избежать
Какие средства синхронизации потоков существуют: mutex, semaphore, NSLock
Что такое кэш и как он работает
Что такое Map и Set в JavaScript
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью