Какие способы борьбы с data race существуют
Что проверяют
Отличаете ли вы data race от race condition и знаете ли современный инструментарий Swift, а не только NSLock. Следом почти всегда спрашивают про акторы и строгую проверку конкурентности в Swift 6.
Как отвечать
Сначала определение: data race это одновременный доступ к одной памяти из нескольких потоков без синхронизации, где хотя бы один поток пишет. Race condition шире: логическая гонка, которая остаётся и при синхронизированном доступе. Затем способы, от предпочтительных:
- компилятор: режим Swift 6 превращает возможные гонки данных в ошибки компиляции через проверку
Sendableи изоляцию. В Swift 6.2 модуль приложения можно по умолчанию изолировать на главном акторе; - акторы:
actorсериализует доступ к своему состоянию,@MainActorзакрепляет код за главным потоком; - неизменяемость и value types:
structиletвместо общего изменяемого класса. Защищать нечего, если данные никто не меняет; - блокировки:
MutexиAtomicиз модуля Synchronization (iOS 18+),OSAllocatedUnfairLock(iOS 16+),NSLock. Хранитьos_unfair_lockкак обычное свойство в Swift небезопасно: адрес структуры не стабилен; - GCD: последовательная очередь или конкурентная с флагом
.barrierдля записи, это схема «много читателей, один писатель».
Назовите и инструмент проверки: Thread Sanitizer в настройках схемы Xcode ловит гонки во время прогона тестов и приложения.
Где спотыкаются
Предлагают DispatchSemaphore вместо мьютекса и используют его внутри async кода. Семафор блокирует поток из кооперативного пула Swift Concurrency, это грозит дедлоком и инверсией приоритетов.
Считают, что актор решает всё. Акторы реентерабельны: пока метод ждёт на await, другой вызов меняет состояние, и проверка «нет в кэше, значит загрузить» снова даёт двойную загрузку.
Глушат предупреждения через @unchecked Sendable и nonisolated(unsafe) без реальной синхронизации или приводят OSAtomicIncrement32. Он устарел ещё в iOS 10, вместо него Atomic или ManagedAtomic из пакета swift-atomics.