Понимание dispatch_async

У меня есть вопрос вокруг этого кода

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSData* data = [NSData dataWithContentsOfURL: 
      kLatestKivaLoansURL];
    [self performSelectorOnMainThread:@selector(fetchedData:) 
      withObject:data waitUntilDone:YES];
});

Первый параметр этого кода:

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 

Мы просим этот код выполнять последовательные задачи в глобальной очереди, чье определение состоит в том, что оно возвращает глобальную параллельную очередь заданного уровня приоритета?

В чем преимущество использования dispatch_get_global_queue в главной очереди?

Я смущен. Не могли бы вы помочь мне понять это лучше.

Ответ 1

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

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

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    //Background Thread
    dispatch_async(dispatch_get_main_queue(), ^(void){
        //Run UI Updates
    });
});

Ответ 2

Все очереди DISPATCH_QUEUE_PRIORITY_X являются параллельными очередями (что означает, что они могут выполнять сразу несколько задач) и являются FIFO в том смысле, что задачи в заданной очереди начнут выполняться с использованием "первого в первом порядке". Это по сравнению с основной очередью (из dispatch_get_main_queue()), которая является последовательной очередью (задачи начнут выполнять и закончить выполнение в том порядке, в котором они получены).

Итак, если вы отправите 1000 dispatch_async() блоков в DISPATCH_QUEUE_PRIORITY_DEFAULT, эти задачи начнут выполняться в том порядке, в котором вы отправили их в очередь. Аналогично для очередей HIGH, LOW и BACKGROUND. Все, что вы отправляете в любую из этих очередей, выполняется в фоновом режиме на альтернативных потоках, вдали от основного потока приложений. Поэтому эти очереди подходят для выполнения таких задач, как загрузка фона, сжатие, вычисление и т.д.

Обратите внимание, что порядок выполнения - это FIFO для каждой очереди. Поэтому, если вы отправляете 1000 задач dispatch_async() в четыре разные параллельные очереди, равномерно распределяя их и отправляя их в BACKGROUND, LOW, DEFAULT и HIGH по порядку (т.е. планируете последние 250 задач в очереди HIGH), очень вероятно, что первые задачи, которые вы видите, запускаются в этой HIGH-очереди, поскольку система взяла на себя ответственность за то, что эти задачи должны как можно быстрее добраться до ЦП.

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

В соответствии с Apple:

https://developer.apple.com/library/content/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html

Параллельная очередь отправки полезна, когда у вас есть несколько задач, которые могут выполняться параллельно. Одновременная очередь по-прежнему является очередью в том, что она отменяет задачи в порядке очередности, первого порядка; однако одновременная очередь может отменить дополнительные задачи до завершения любых предыдущих задач. Фактическое количество задач, выполняемых параллельной очередью в любой момент времени, является переменной и может динамически изменяться по мере изменения условий в приложении. Многие факторы влияют на количество задач, выполняемых параллельными очередями, включая количество доступных ядер, объем работы, выполняемой другими процессами, а также количество и приоритет задач в других очередях последовательной отправки.

В принципе, если вы отправляете эти 1000 блоков dispatch_async() в очередь DEFAULT, HIGH, LOW или BACKGROUND, все они будут запускаться в том порядке, в котором вы их отправляете. Однако более короткие задачи могут заканчиваться до более длинных. Причинами этого являются наличие доступных ядер процессора или если текущие задачи очереди выполняют вычислительно-неинтенсивную работу (что заставляет систему думать, что она может отправлять дополнительные задачи параллельно независимо от количества ядер).

Уровень concurrency полностью обрабатывается системой и основан на нагрузке системы и других внутренних факторах. Это красота Grand Central Dispatch (система dispatch_async()) - вы просто делаете свои рабочие единицы как кодовые блоки, устанавливаете для них приоритет (на основе выбранной очереди) и позволяете системе обрабатывать остальные.

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

"Основная" очередь с другой стороны (из dispatch_get_main_queue()) является последовательной очередью (не параллельной). Задачи, отправленные в основную очередь, всегда выполняются по порядку и всегда будут заканчиваться по порядку. Эти задачи также будут выполняться в потоке пользовательского интерфейса, поэтому он подходит для обновления вашего пользовательского интерфейса с сообщениями о ходе выполнения, уведомлениями о завершении и т.д.

Ответ 3

Быстрая версия

Это версия Swift версии David Objective-C. Вы используете глобальную очередь для запуска объектов в фоновом режиме и в основной очереди для обновления пользовательского интерфейса.

Swift 3

DispatchQueue.global(qos: .background).async {

    // Background Thread

    DispatchQueue.main.async {
        // Run UI Updates
    }
}