В Swift для обеспечения потокобезопасности часто используются GCD (Grand Central Dispatch) и очереди (DispatchQueue), NSLock, а также современные механизмы, такие как actor (начиная с Swift 5.5).
Основная идея – доступ к изменяемым данным происходит либо строго через последовательные очереди, либо с использованием синхронизаторов (замков).
Пример с GCD:
class ThreadSafeArray<Element> { private var array: [Element] = [] private let queue = DispatchQueue(label: "com.example.arrayQueue", attributes: .concurrent) func append(_ item: Element) { queue.async(flags: .barrier) { self.array.append(item) } } func get(index: Int) -> Element? { var result: Element? queue.sync { result = self.array.indices.contains(index) ? self.array[index] : nil } return result } }
Современный подход: Actors (Swift 5.5+):
actor SafeCounter { private var value = 0 func increment() { value += 1 } func get() -> Int { return value } }
Оба подхода позволяют избежать гонок данных и сохранять согласованность состояния.
Вопрос:
Если вы используете
DispatchQueue.syncвнутри Main Queue, что произойдет и почему?
Ответ: Возникнет дедлок (Deadlock), потому что Main Queue уже выполняет задачу и ждет завершения синхронной операции на той же самой очереди. Следовательно, очередь не сможет обработать следующую задачу, пока текущая не завершится, что никогда не произойдет.
Пример:
DispatchQueue.main.sync { // Дедлок: эта строка никогда не выполнится }
История
В проекте использовалась изменяемая коллекция, к которой обращались одновременно из нескольких потоков без синхронизации. Это приводило к падениям приложения и трудноуловимым багам из-за гонки данных: периодически терялись элементы, иногда происходил выход за границы массива.
История
Разработчик некорректно использовал DispatchQueue.sync на главной очереди в рантайме, что вызвало дедлок и полный стоп UI у пользователей после выхода обновления. Требовался срочный фикс и откат релиза.
История
При попытке сделать класс потокобезопасным с помощью NSLock, забыли реализовать lock/unlock для всех путей выхода из метода (например, в случае ошибки или return внутри guard), что приводило к потенциалу дедлоков и серьезным проблемам с производительностью приложения.