Какие проблемы возникают при одновременной записи в массив из нескольких потоков в Swift?

«Какие проблемы возникают при одновременной записи в массив из нескольких потоков в Swift?» — вопрос из категории Многопоточность, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Проблема: Стандартные массивы Swift (Array) не являются потокобезопасными. Одновременная модификация из нескольких потоков без синхронизации приводит к состоянию гонки (race condition).

Последствия:

  1. Повреждение данных (Data Race): Внутренняя структура массива может быть нарушена, что приводит к неопределенному поведению, включая сбои приложения.
  2. Потеря обновлений: Записи, сделанные одним потоком, могут быть перезаписаны другим.
  3. Некорректное состояние: Массив может содержать неожиданные или противоречивые значения.

Решение — синхронизация доступа:

  • Использование DispatchQueue (барьер или серийная очередь):

    let syncQueue = DispatchQueue(label: "com.example.sync", attributes: .concurrent)
    var threadSafeArray = [Int]()
    
    // Запись с барьером (эксклюзивный доступ)
    syncQueue.async(flags: .barrier) {
        threadSafeArray.append(42)
    }
    
    // Чтение (параллельный доступ)
    syncQueue.sync {
        print(threadSafeArray.last)
    }
  • Использование NSLock или pthread_mutex_t:

    let lock = NSLock()
    var array = [Int]()
    
    lock.lock()
    array.append(42)
    lock.unlock()
  • Использование Actor (Swift 5.5+):

    actor SafeArrayContainer {
        private var array = [Int]()
    
        func append(_ value: Int) {
            array.append(value)
        }
    
        func getLast() -> Int? {
            return array.last
        }
    }
    
    // Использование
    let container = SafeArrayContainer()
    Task {
        await container.append(42)
    }

Альтернативы: Использование специализированных потокобезопасных структур данных (например, из NSCache для кэширования или реализация собственной обертки).