Что такое concurrent queue в GCD?
Что проверяют
Понимаете ли вы разницу между порядком запуска и порядком завершения задач, и осознаёте ли цену параллельности. Тема быстро уходит от определения к гонкам, дедлокам и перерасходу потоков, и именно там проверяют инженерную зрелость.
Как отвечать
Любая очередь в GCD забирает задачи в порядке поступления. Последовательная очередь запускает следующую задачу только после завершения предыдущей. Параллельная, созданная с атрибутом .concurrent, тоже начинает задачи по очереди, но не ждёт завершения предыдущих, поэтому одновременно выполняется несколько блоков, а порядок их окончания произвольный. Главная очередь всегда последовательная, глобальные очереди по уровням качества обслуживания параллельные.
Сама по себе параллельная очередь ничего не синхронизирует. Классический приём защиты общего состояния это схема с барьером на собственной очереди: чтение через sync, запись через async с флагом барьера. На глобальных очередях барьер не даёт эксклюзивности, для него нужна своя очередь.
let queue = DispatchQueue(label: "cache", attributes: .concurrent)
func read(_ k: String) -> Data? { queue.sync { storage[k] } }
func write(_ k: String, _ v: Data) { queue.async(flags: .barrier) { storage[k] = v } }Ждать завершения набора задач следует через DispatchGroup, а не через барьер: барьер нужен для эксклюзивной записи, а не для ожидания.
Где спотыкаются
Кладут в параллельную очередь блокирующие вызовы: сон, ожидание семафора, синхронный сетевой запрос. Пул видит занятые потоки и создаёт новые, число потоков растёт лавиной, приложение упирается в лимит и деградирует ровно там, где ждали ускорения.
Вызывают sync на очереди, в которой уже выполняются. Это мгновенный дедлок, и самый частый его вид это синхронный переход на главную очередь из главного потока. Асинхронный вызов в той же ситуации безопасен.
Считают, что параллельная очередь решает вопрос гонок сама, либо ставят барьер на глобальную очередь и уверены, что запись защищена. Ни того, ни другого нет: общий изменяемый объект нужно защищать явно, и только на очереди, которой вы владеете.