Я вижу, что Swift поддерживает 2 вида фоновых задач.
Используя DispatchQueue
DispatchQueue.global(qos: .background).async {}
Используя Task
Task(priority: .background) {}
Итак, я не понимаю, в чем разница и какой из них мне следует использовать?





Task — это относительно новый API, который является частью Swift Concurrency , тогда как DispatchQueue существуют уже давно. Дополнительную информацию см. в документации.
Если вы хотите использовать Swift Concurrency в своем коде (например, писать функции async вместо использования шаблона обработчиков завершения), вам следует использовать Task и связанные с ним API. Как правило, смешивать Swift Concurrency с очередями отправки не рекомендуется. Это очень разные API с очень разными базовыми концепциями.
В частности, для двух показанных вами фрагментов кода основное различие заключается в том, что DispatchQueue.global() не запускает ваш код в основном потоке, тогда как Task { ... } запускает ваш код в основном потоке, если вы вызываете его из изолированного MainActor контекста. То есть Task.init наследует контекст актера. Чтобы запустить Task из основного потока, используйте Task.detached.
Это два совершенно разных шаблона (несмотря на внешне схожую ссылку на приоритет .backgroundQoS /). Эти два подхода используются в совершенно разных ситуациях. Это сильное упрощение, но:
Пример очереди отправки используется в устаревших кодовых базах GCD (тех, которые (еще) не приняли «Swift concurrency»), и вы хотите запустить единицу работы, которая сама по себе является синхронной, но вы хотите избежать блокировки текущий поток; и
Task {…} используется в современных Swift concurrency базах кода, как правило, для запуска задачи, которая сама по себе состоит из асинхронного кода (в частности, кода, который будет await каким-то другим async методом)… мы используем Task {…} либо для перехода от синхронного контекста к асинхронный или насладиться более детальным контролем неструктурированного параллелизма (ценой большей сложности).
Итак, вопрос более фундаментальный, чем DispatchQueue.global.async {…} vs Task {…}. Настоящий вопрос — параллелизм GCD и Swift. (И мы почти всегда избегаем смешивания GCD с параллелизмом Swift, за исключением случаев, когда это абсолютно необходимо, но это выходит за рамки этого вопроса. Обычно мы выбираем параллелизм GCD или Swift и последовательно используем этот конкретный технологический стек в рамках данного проекта.)
В современных базах кода мы бы приняли параллелизм Swift (и, таким образом, отдали бы предпочтение Task {…} или, где это возможно, придерживались структурированного параллелизма), но если вы обременены некоторой устаревшей кодовой базой GCD, то вы можете использовать глобальные очереди отправки для медленного/синхронного работа.
Кстати, я бы посоветовал немного разобраться с термином «фоновая задача». В частности, я мог бы посоветовать вам не путать этот термин с параллелизмом Swift TaskPriority .background или глобальной очередью отправки с QoSClass .background).
Традиционно, когда мы абстрактно говорим о «фоновой задаче», мы имеем в виду «вытащить ее из текущего потока». Но «приоритет» задачи и/или очередь отправки «качество обслуживания» — это скорее вопрос относительного приоритета этой работы по отношению к другой также представленной работе.
Наконец, стоит отметить, что Task {…} не работает вне текущего контекста. На самом деле всё наоборот: Задача {…} будет запускать работу от имени текущего актера. Использование Task.detached {…}, вероятно, ближе к идее глобальной очереди отправки, но даже это несовершенная аналогия.
Для получения дополнительной информации см. Язык программирования Swift: Параллелизм: неструктурированный параллелизм или видеоролики WWDC 2021 Встречайте async/await и Swift concurrency: За кулисами.