> В чем разница последовательной и параллельной очереди (iOS, Swift)

Уровень: senior · Роль: mobile · Категория: Технические вопросы

Компании: 2GIS

Стек: iOS, Swift

> Пример ответа

Короткий ответ

Последовательная очередь (serial) выполняет задачи по одной, в порядке добавления, пока параллельная (concurrent) может запускать несколько задач одновременно. В GCD последовательная очередь гарантирует порядок выполнения и отсутствие гонок данных, но не использует преимущества многопоточности. Параллельная очередь повышает пропускную способность, но требует синхронизации доступа к общим ресурсам. Выбор зависит от задачи: сериализация нужна для защиты состояния, параллелизм - для независимых вычислений.

Подробное объяснение

В GCD очередь - это абстракция над потоками. Последовательная очередь (например, DispatchQueue.main или созданная через DispatchQueue(label:) без атрибута .concurrent) выполняет блоки строго по одному. Это значит, что следующий блок не начнётся, пока предыдущий не завершится. Порядок FIFO соблюдается всегда.

Параллельная очередь (DispatchQueue(label:attributes: .concurrent)) может запускать несколько блоков одновременно. GCD сам управляет пулом потоков, и количество одновременных задач зависит от системных ресурсов. Порядок начала выполнения не гарантирован, но блоки добавляются в очередь в порядке вызова.

Ключевое отличие - модель синхронизации. В serial очереди нет гонок данных, потому что только один блок выполняется в момент времени. В concurrent очереди два блока могут одновременно читать и писать в одну переменную - это приводит к data race. Поэтому нужны барьеры (barrier), семафоры или другие механизмы.

Важно: параллельная очередь не означает, что каждый блок выполняется в отдельном потоке. GCD переиспользует потоки, и количество потоков ограничено. Также параллельная очередь не гарантирует ускорение - если задачи блокируют I/O или зависят друг от друга, выигрыша не будет.

На практике

В iOS-разработке serial очереди используются для:

  • защиты общего состояния (например, кэша, модели данных)
  • гарантии порядка операций (например, запись в файл)
  • работы с CoreData (контекст должен быть сериализован)

Concurrent очереди применяются для:

  • параллельной обработки независимых данных (например, загрузка нескольких изображений)
  • тяжёлых вычислений, которые не трогают общие ресурсы
  • комбинирования с DispatchGroup для ожидания завершения группы задач

Частая практика - паттерн "barrier + concurrent queue": чтения идут параллельно, а записи - через queue.async(flags: .barrier), что гарантирует эксклюзивный доступ на запись, но сохраняет параллельность чтения.

Пример кода

SWIFT
// Serial queue
let serialQueue = DispatchQueue(label: "com.example.serial")
serialQueue.async {
print("Task 1")
}
serialQueue.async {
print("Task 2")
}
// Вывод всегда: Task 1, Task 2
// Concurrent queue
let concurrentQueue = DispatchQueue(label: "com.example.concurrent", attributes: .concurrent)
concurrentQueue.async {
print("Task A")
}
concurrentQueue.async {
print("Task B")
}
// Порядок вывода не гарантирован: может быть A, B или B, A
// Barrier для безопасной записи
concurrentQueue.async(flags: .barrier) {
// эксклюзивная запись
self.cache[key] = value
}

Как отвечать на собеседовании

Начни с чёткого определения: serial - одна задача за раз, concurrent - несколько одновременно. Затем переходи к практическим следствиям: порядок выполнения, гонки данных, производительность. Упомяни, что GCD управляет потоками, и параллельная очередь не равна "много потоков на каждый блок". Приведи пример из реальной задачи: защита кэша через barrier, параллельная загрузка изображений. Если спросят про DispatchQueue.main - подчеркни, что это serial очередь, и её нельзя использовать для тяжёлых задач. Хорошо показать понимание trade-off: serial безопаснее, но медленнее; concurrent быстрее, но требует синхронизации.

Что проверяет интервьюер

Интервьюер оценивает:

  • понимание базовых механизмов GCD, а не заученных определений
  • умение объяснить, когда нужен serial, а когда concurrent
  • знание проблем параллелизма: data race, deadlock, priority inversion
  • способность связать теорию с практикой iOS-разработки
  • осознание, что порядок в concurrent очереди не гарантирован, и как это влияет на дизайн

Типичные ошибки

  • Утверждение, что concurrent очередь всегда быстрее - на самом деле при малом количестве задач или при блокировках выигрыша нет
  • Игнорирование гонок данных: "я использую concurrent, и всё работает" - это не значит, что нет data race
  • Путаница между DispatchQueue и OperationQueue - это разные уровни абстракции
  • Использование DispatchQueue.main для длительных операций - блокирует UI
  • Забывают про autoreleasepool в циклах внутри concurrent задач - это приводит к росту памяти
  • Неправильное понимание barrier: он работает только в concurrent очереди, в serial он не имеет смысла

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью