Может ли порядок выполнения задач в параллельной очереди меняться в зависимости от условий
Что проверяют
Различаете ли вы порядок запуска и порядок завершения задач и знаете ли, чем GCD отличается от OperationQueue. Следом обычно спрашивают, как гарантировать нужный порядок, не превращая всё в последовательную очередь.
Как отвечать
Начните с GCD. Параллельная DispatchQueue выдаёт задачи в том порядке, в котором их добавили, это FIFO на выходе из очереди. Но задачи сразу расходятся по разным потокам, поэтому порядок завершения не гарантирован. Он зависит от длительности задачи, числа свободных потоков, QoS и загрузки системы.
У OperationQueue порядок не FIFO даже формально. Операция стартует, когда она готова (isReady), а на это влияют:
- зависимости:
addDependency(_:)не даст операции начаться, пока не завершатся все, от которых она зависит; - приоритет:
queuePriorityиqualityOfServiceпозволяют поздней операции обогнать ранние; - лимит:
maxConcurrentOperationCount = 1убирает параллельность, но порядок всё равно определяют готовность и приоритет.
Способы управлять порядком:
- барьер:
async(flags: .barrier)на своей параллельной очереди ждёт завершения предыдущих задач и выполняется в одиночку. Классическая схема «много читателей, один писатель»; DispatchGroup:notifyсрабатывает, когда закончены все задачи группы;- Swift Concurrency:
async letиwithTaskGroup. Результаты группы вfor awaitприходят в порядке завершения, и если нужен исходный порядок, возвращайте индекс вместе со значением.
Где спотыкаются
Ставят барьер на глобальную очередь. На DispatchQueue.global() флаг .barrier не работает, и задача выполняется как обычная.
Добавляют результаты параллельных задач в общий массив. Порядок элементов оказывается случайным, а сам Array не потокобезопасен, и это гонка данных, которую ловит Thread Sanitizer.
Проверяют порядок в тестах через sleep или asyncAfter. На медленной машине CI такой тест мигает, правильнее ждать через XCTestExpectation или await.