Разница между DispatchQueue.main.async и DispatchQueue.main.sync

Я использую DispatchQueue.main.async, так как долгое время выполнял некоторые связанные с UI операции. Но Swift предоставляет DispatchQueue.main.async и DispatchQueue.main.sync, и оба они выполняются в основной очереди. Так может ли кто-нибудь сказать мне разницу между ними? И когда я должен их использовать? Заранее благодарю вас.

        DispatchQueue.main.async {
            self.imageView.image = imageView
            self.lbltitle.text = ""

        }
        DispatchQueue.main.sync {
            self.imageView.image = imageView
            self.lbltitle.text = ""

        }

Ответы

Ответ 1

Почему параллелизм? Как только вы добавляете в приложение тяжелые задачи, такие как загрузка данных, это замедляет работу пользовательского интерфейса или даже останавливает его. Параллелизм позволяет выполнять 2 или более задач "одновременно". Недостаток этого подхода заключается в том, что безопасность потоков не всегда так просто контролировать. F.E. когда разные задачи хотят получить доступ к одним и тем же ресурсам, например попытаться изменить одну и ту же переменную в разных потоках или получить доступ к ресурсам, уже заблокированным разными потоками.

Есть несколько абстракций, о которых нам нужно знать.

  • Очереди.
  • Синхронное/Асинхронное выполнение задач.
  • Приоритеты.
  • Общие проблемы.

Очереди.

Должен быть последовательным или одновременным. Кроме глобального или частного одновременно.

С последовательными очередями задачи будут завершаться одна за другой, в то время как с параллельными очередями задачи будут выполняться одновременно и будут выполняться по неожиданным графикам. Та же группа задач займет намного больше времени в последовательной очереди по сравнению с параллельной очередью.

Вы можете создавать свои собственные частные очереди (как последовательные, так и одновременные) или использовать уже доступные глобальные (системные) очереди. Основная очередь является единственной последовательной очередью из всех глобальных очередей.

Настоятельно рекомендуется не выполнять сложные задачи, которые не относятся к работе пользовательского интерфейса в основной очереди (например, загружать данные из сети), а вместо этого выполнять их в других очередях, чтобы пользовательский интерфейс не был заморожен и реагировал на действия пользователя. Если мы позволим изменить пользовательский интерфейс в других очередях, изменения могут быть сделаны с другим и неожиданным графиком и скоростью. Некоторые элементы пользовательского интерфейса могут быть нарисованы до или после того, как они необходимы. Это может привести к сбою пользовательского интерфейса. Мы также должны помнить, что, поскольку глобальные очереди являются системными очередями, система может выполнять с ними некоторые другие задачи.


Качество обслуживания/приоритет.

Очереди также имеют различное qos (качество обслуживания), в котором задается задача, выполняющая приоритет (от наивысшего к низшему здесь):
.userInteractive - основная очередь
.userInitiated - для задач, инициированных пользователем, для которых пользователь ожидает ответа
.utility - для задач, которые занимают некоторое время и не требуют немедленной ответ, например, работа с данными
.background - для задач, которые не связаны с визуальной частью и не являются строгими по времени выполнения).

Существует также

.default очередь, которая не передает информацию о QOS. Если не удалось обнаружить qos, qos будет использоваться между .userInitiated и .utility.

Задачи могут выполняться синхронно или асинхронно.

  • Синхронная функция возвращает управление в текущую очередь только после завершения задачи. Он блокирует очередь и ожидает завершения задачи.

  • Асинхронная функция возвращает управление текущей очереди сразу после того, как задача была отправлена для выполнения в другой очереди. Он не ждет, пока задача не будет завершена. Это не блокирует очередь.

Общие проблемы.

Наиболее распространенные ошибки, которые программисты допускают при проектировании параллельных приложений, следующие:

  • Состояние гонки - возникает, когда работа приложения зависит от порядка выполнения частей кода.
  • Инверсия приоритетов - когда задачи с более высоким приоритетом ожидают завершения задач с меньшим приоритетом из-за блокировки некоторых ресурсов
  • Тупик - когда несколько очередей бесконечно ждут источников (переменных, данных и т.д.), Уже заблокированных некоторыми из этих очередей.

НИКОГДА не вызывайте функцию синхронизации в главной очереди.
Если вы вызовите функцию синхронизации в главной очереди, она заблокирует очередь, а также очередь будет ожидать завершения задачи, но задача никогда не будет завершена, поскольку она даже не сможет запуститься из-за очереди. уже заблокирован. Это называется тупик.

Когда использовать синхронизацию? Когда нам нужно подождать, пока задача не будет завершена. F.E. когда мы проверяем, что некоторая функция/метод не вызывается дважды. F.E. у нас есть синхронизация и мы пытаемся предотвратить двойной вызов до тех пор, пока он полностью не завершится. Вот некоторый код для этой проблемы:
Какузнать, что вызвало сообщение об ошибке сбоя на устройстве IOS?

Ответ 2

Когда вы используете async, он позволяет очереди вызовов двигаться без ожидания до тех пор, пока не будет выполнен отправленный блок. Наоборот, sync заставит очередь вызова остановиться и дождитесь завершения работы, которую вы отправили в блок. Поэтому sync может привести к взаимоблокировкам. Попробуйте запустить DispatchQueue.main.sync из основной очереди, и приложение затормозится, потому что очередь ожидания будет ждать, пока отправленный блок не будет завершен, но он даже не сможет начать (поскольку очередь остановлена ​​и ждет)

Когда использовать sync? Когда вам нужно ждать чего-то, сделанного в РАЗЛИЧНОЙ очереди, и только затем продолжить работу над вашей текущей очередью

Пример использования синхронизации:

В последовательной очереди вы можете использовать sync как мьютекс, чтобы удостовериться, что только один поток может одновременно выполнять защищенный фрагмент кода.