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

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

Ответ

Потокобезопасный массив гарантирует корректность данных при конкурентном доступе из нескольких потоков. В Swift для этого можно использовать GCD (Grand Central Dispatch) с барьерными операциями.

Основной подход: Использовать DispatchQueue с атрибутом .concurrent.

  • Запись выполняется асинхронно с флагом .barrier, что делает её эксклюзивной.
  • Чтение выполняется синхронно для получения актуальных данных.
class ThreadSafeArray<Element> {
    private var array: [Element] = []
    private let queue = DispatchQueue(label: "threadSafe.array.queue", attributes: .concurrent)

    func append(_ element: Element) {
        queue.async(flags: .barrier) {
            self.array.append(element)
        }
    }

    var elements: [Element] {
        return queue.sync {
            return self.array
        }
    }
}

Почему это работает? Барьерная задача (flags: .barrier) ждёт завершения всех текущих задач в очереди и выполняется в одиночку, блокируя другие задачи на время записи. Это обеспечивает безопасность записи. Синхронное чтение (queue.sync) гарантирует, что возвращаемые данные будут соответствовать состоянию на момент вызова.

Альтернативы и нюансы:

  • Для сценариев с интенсивной записью или более тонкого контроля можно использовать низкоуровневые примитивы: NSLock, os_unfair_lock или pthread_rwlock_t.
  • Реализация выше оптимизирована для частого чтения и редкой записи. Для полного набора операций (удаление, индексация) потребуется добавить аналогичные потокобезопасные методы.