Вопрос проверяет знание различных механизмов для безопасного доступа к общим ресурсам из нескольких потоков.
Инструменты синхронизации предотвращают состояние гонки (race condition) при одновременном доступе к данным. Основные: DispatchSemaphore ограничивает количество потоков, имеющих доступ. DispatchBarrier обеспечивает эксклюзивный доступ для записи в concurrent очереди. Mutex (NSLock, pthread_mutex) блокирует критическую секцию, позволяя работать только одному потоку. Actors — современный механизм из Swift Concurrency, который изолирует состояние и позволяет безопасно к нему обращаться через await.
Проблема: Когда несколько потоков одновременно читают и пишут в одну переменную, результат может быть непредсказуемым.
Решения:
Семафор (DispatchSemaphore):
Позволяет ограничить доступ к ресурсу N потокам.
wait() уменьшает счетчик, signal() — увеличивает. Если счетчик равен нулю, поток ждет.
let semaphore = DispatchSemaphore(value: 1) // Разрешаем доступ 1 потоку
func criticalSection() {
semaphore.wait() // Входим в секцию
// Работа с общим ресурсом...
semaphore.signal() // Выходим из секции
}Барьер (DispatchBarrier) в concurrent очереди:
Задачи, отправленные с флагом .barrier, выполняются эксклюзивно.
let concurrentQueue = DispatchQueue(label: "concurrent", attributes: .concurrent)
// Чтение может быть параллельным
concurrentQueue.async { /* читаем данные */ }
// Запись будет эксклюзивной
concurrentQueue.async(flags: .barrier) { /* пишем данные */ }Мьютекс (Mutex):
Блокирует участок кода. NSLock — простой в использовании мьютекс.
let lock = NSLock()
func criticalSection() {
lock.lock()
// Работа с общим ресурсом...
lock.unlock()
}Акторы (Actors) (Swift 5.5+):
Актор — это ссылочный тип, который защищает свои данные от одновременного доступа. Все доступы к его свойствам и методам извне автоматически сериализуются.
actor Counter {
private var value = 0
func increment() {
value += 1
}
func getValue() -> Int {
return value
}
}
// Использование
let counter = Counter()
Task {
await counter.increment() // Доступ к методу актора через `await`
let value = await counter.getValue()
}