Какой порядок вывода при последовательной и конкурентной очереди
Что проверяют
Понимаете ли вы, что гарантирует очередь, а что нет: порядок запуска, порядок завершения, блокировку вызывающего потока. Обычно показывают фрагмент с async и sync вперемешку и просят назвать вывод, а следом спрашивают, что будет при sync на ту же очередь.
Как отвечать
Разведите два типа:
- последовательная: задачи стартуют в порядке добавления, следующая начинается только после завершения предыдущей, поэтому вывод совпадает с порядком постановки.
DispatchQueue(label:)без атрибутов иDispatchQueue.mainпоследовательные; - конкурентная: задачи тоже забираются по порядку, но выполняются одновременно, поэтому порядок завершения и вывода не гарантирован. Глобальные очереди
DispatchQueue.global(qos:)конкурентные.
Типичный пример на последовательной очереди q:
print(1)
q.async { print(2) }
q.sync { print(3) }
print(4)Вывод строго 1 2 3 4: sync ставит блок в конец очереди и ждёт, а очередь сначала выполнит блок с 2. Если q конкурентная, гарантировано только, что 1 будет первой, а 4 выйдет после 3. Двойка окажется где угодно после единицы.
Ещё один частый вариант: на главном потоке print(1), затем DispatchQueue.main.async { print(2) }, затем print(3) дадут 1 3 2. Блок выполнится, только когда текущий код вернёт управление циклу событий.
Упорядочить работу в конкурентной очереди помогает флаг .barrier: барьерный блок ждёт завершения предыдущих и выполняется в одиночку. Работает он только на собственных конкурентных очередях, на глобальных ведёт себя как обычный async.
Где спотыкаются
Говорят, что конкурентная очередь выведет задачи в обратном или случайном порядке. Правильно «не гарантирован»: на коротких блоках часто получается порядок добавления, и в этом ловушка.
Вызывают sync на последовательную очередь изнутри неё самой, включая DispatchQueue.main.sync с главного потока. Это дедлок.
Считают, что последовательная очередь привязана к одному потоку. GCD может выполнять её блоки на разных потоках, гарантируется только отсутствие параллелизма.