Понимание dispatch_async

233

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

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над основной очередью?

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

user2332873
источник
1
Вам лучше разрезать код на несколько строк, чтобы он стал более понятным. безопасный dispatch_get_global_queueвнутри переменный тип dispatch_queue_t myQueue. Это более читабельно, передавая только myQueue вашему `` dispatch_async` "
Alex Cio

Ответы:

517

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

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

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    //Background Thread
    dispatch_async(dispatch_get_main_queue(), ^(void){
        //Run UI Updates
    });
});
Дэвид
источник
Я понимаю, что Дэвид спасибо за ваш ответ, но мой вопрос был больше в том, чтобы понять логику этого, то есть просить этот код выполнять последовательные задачи в глобальной очереди, которая является самой параллельной очередью
user2332873
Я делаю именно то, что вы предлагаете, но каким-то образом uiTableViewCell не обновляется сразу же, когда я вызываю [self.tableView reloadData] в Run UI Updates. Это занимает около 4 или 5 секунд. Это сводит меня с ума уже несколько дней .
GrandSteph
@GrandSteph Я не слишком знаком с этим методом. Может быть, этот метод занимает всего 5 секунд для запуска. Что важно с dispatch_async, так это то, что он позволяет вам работать в фоновом режиме, не вешая основной поток.
Дэвид
2
что 0означает?
Мед
3
@Honey 0 - это flagsпараметр, который в настоящее время абсолютно ничего не делает. Из документов:Flags that are reserved for future use. Always specify 0 for this parameter.
Дэвид
199

Все очереди 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), очень вероятно, что первые задачи, которые вы запускаете, будут в этой ВЫСОКОЙ очереди, поскольку система поняла, что эти задачи должны как можно быстрее попасть в ЦП.

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

Согласно Apple:

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

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

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

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

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

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

SimplePanda
источник
+1, но я думаю, что на практике не имеет большого значения, являются ли параллельные очереди FIFO или просто случайным порядком. Если вы запускаете 5 задач в цикле, предположите, что они по сути начнутся одновременно. Нет гарантии, что, например, первая операция ввода-вывода в 1-й задаче произойдет до 5-й, даже если они выполняют один и тот же код. OTOH, для последовательных очередей, поведение FIFO является существенным, и IMHO, это определяющая разница между двумя типами очередей.
Герхард Уесп
Неверное объяснение. Хлопает много!
Okhan Okbay
36

Swift версия

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

DispatchQueue.global(qos: .background).async {
    
    // Background Thread
    
    DispatchQueue.main.async {
        // Run UI Updates
    }
}
Suragch
источник