Noções básicas sobre dispatch_async

233

Tenho uma pergunta em torno deste código

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

O primeiro parâmetro desse código é

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 

Estamos solicitando que esse código execute tarefas seriais na fila global cuja definição em si é que ele retorne a fila simultânea global de um determinado nível de prioridade?

Qual é a vantagem de usar dispatch_get_global_queuesobre a fila principal?

Estou confuso. Poderia me ajudar a entender isso melhor.

user2332873
fonte
1
Você deve cortar melhor seu código em várias linhas, para que faça mais sentido. seguro seu dispatch_get_global_queuedentro de um tipo variável de dispatch_queue_t myQueue. É mais legível passar apenas o myQueue para o seu `` dispatch_async` #
Alex Cio

Respostas:

517

O principal motivo pelo qual você usa a fila padrão sobre a fila principal é executar tarefas em segundo plano.

Por exemplo, se eu estiver baixando um arquivo da Internet e quiser atualizar o usuário no andamento do download, executarei o download na fila padrão de prioridade e atualizarei a UI na fila principal de forma assíncrona.

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    //Background Thread
    dispatch_async(dispatch_get_main_queue(), ^(void){
        //Run UI Updates
    });
});
David
fonte
Eu entendo que david obrigado por sua resposta, mas minha pergunta foi mais para entender a lógica de fazer isso, ou seja, está pedindo para que este código execute tarefas seriais na fila global que é a fila simultânea
#
Estou fazendo exatamente o que você sugere, mas, de alguma forma, o uiTableViewCell não é atualizado imediatamente quando eu chamo [self.tableView reloadData] nas atualizações da interface do usuário Executar. Leva cerca de 4 ou 5 segundos. Isso me deixa louco há vários dias .
GrandSteph
@GrandSteph Não estou muito familiarizado com esse método. Talvez esse método leve apenas 5 segundos para ser executado. O importante com o dispatch_async é que ele permite que você faça coisas em segundo plano sem interromper o thread principal.
David
2
o que 0significa?
Mel
3
@Honey O 0 é o flagsparâmetro, que atualmente não faz absolutamente nada. Da documentação:Flags that are reserved for future use. Always specify 0 for this parameter.
David
199

Todas as filas DISPATCH_QUEUE_PRIORITY_X são filas simultâneas (o que significa que podem executar várias tarefas ao mesmo tempo) e são FIFO no sentido de que as tarefas em uma determinada fila começarão a ser executadas usando a ordem "primeiro a entrar, primeiro a sair". Isso se compara à fila principal (de dispatch_get_main_queue ()), que é uma fila serial (as tarefas começarão a ser executadas e terminarão na ordem em que são recebidas).

Portanto, se você enviar 1000 blocos dispatch_async () para DISPATCH_QUEUE_PRIORITY_DEFAULT, essas tarefas começarão a ser executadas na ordem em que você as enviou para a fila. Da mesma forma para as filas HIGH, LOW e BACKGROUND. Tudo o que você envia para qualquer uma dessas filas é executado em segundo plano em threads alternativos, longe do thread principal do aplicativo. Portanto, essas filas são adequadas para a execução de tarefas como download em segundo plano, compactação, computação etc.

Observe que a ordem de execução é FIFO por fila. Portanto, se você enviar 1000 tarefas dispatch_async () para as quatro filas simultâneas diferentes, dividindo-as uniformemente e enviando-as para BACKGROUND, LOW, DEFAULT e HIGH em ordem (ou seja, você agende as últimas 250 tarefas na fila HIGH), é muito provável que as primeiras tarefas que você vê iniciando serão nessa fila HIGH, já que o sistema implicou que essas tarefas precisam chegar à CPU o mais rápido possível.

Observe também que eu digo "começará a executar em ordem", mas lembre-se de que, como filas simultâneas, as coisas não terminarão necessariamente a execução em ordem, dependendo da duração de cada tarefa.

De acordo com a Apple:

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

Uma fila de expedição simultânea é útil quando você possui várias tarefas que podem ser executadas em paralelo. Uma fila simultânea ainda é uma fila, pois remove da fila as tarefas em uma ordem de entrada, saída; no entanto, uma fila simultânea pode desenfileirar tarefas adicionais antes que as tarefas anteriores sejam concluídas. O número real de tarefas executadas por uma fila simultânea a qualquer momento é variável e pode mudar dinamicamente conforme as condições do aplicativo mudam. Muitos fatores afetam o número de tarefas executadas pelas filas simultâneas, incluindo o número de núcleos disponíveis, a quantidade de trabalho sendo realizado por outros processos e o número e a prioridade de tarefas em outras filas de expedição serial.

Basicamente, se você enviar esses 1000 blocos dispatch_async () para uma fila DEFAULT, HIGH, LOW ou BACKGROUND, todos começarão a ser executados na ordem em que foram enviados. No entanto, tarefas mais curtas podem terminar antes das mais longas. As razões por trás disso são se existem núcleos de CPU disponíveis ou se as tarefas atuais da fila estão executando um trabalho computacionalmente não intensivo (fazendo com que o sistema pense que pode enviar tarefas adicionais em paralelo, independentemente da contagem de núcleos).

O nível de simultaneidade é tratado inteiramente pelo sistema e é baseado na carga do sistema e em outros fatores determinados internamente. Essa é a beleza do Grand Central Dispatch (o sistema dispatch_async ()) - você apenas cria suas unidades de trabalho como blocos de código, define uma prioridade para elas (com base na fila que escolher) e deixa o sistema lidar com o resto.

Então, para responder à sua pergunta acima: você está parcialmente correto. Você está "pedindo esse código" para executar tarefas simultâneas em uma fila simultânea global no nível de prioridade especificado. O código no bloco será executado em segundo plano e qualquer código (semelhante) adicional será executado potencialmente em paralelo, dependendo da avaliação do sistema dos recursos disponíveis.

A fila "principal", por outro lado (de dispatch_get_main_queue ()) é uma fila serial (não simultânea). As tarefas enviadas para a fila principal sempre serão executadas em ordem e sempre serão concluídas em ordem. Essas tarefas também serão executadas no thread da interface do usuário, por isso é adequado para atualizar sua interface do usuário com mensagens de progresso, notificações de conclusão etc.

SimplePanda
fonte
+1, mas acho que, na prática, não importa muito se as filas simultâneas são FIFO ou apenas ordem aleatória. Se você iniciar 5 tarefas em um loop, suponha que elas basicamente sejam iniciadas ao mesmo tempo. Não há garantia de que, por exemplo, a primeira operação de E / S da 1ª tarefa ocorra antes da 5ª, mesmo se eles executarem o mesmo código. OTOH, para filas seriais, o comportamento FIFO é essencial e IMHO é a diferença que define os dois tipos de filas.
Gerhard Wesp
Explicação incrível. Aplausos muito!
Okhan Okbay 26/10
36

Versão Swift

Esta é a versão rápida da resposta Objective-C de David. Você usa a fila global para executar as coisas em segundo plano e a fila principal para atualizar a interface do usuário.

DispatchQueue.global(qos: .background).async {
    
    // Background Thread
    
    DispatchQueue.main.async {
        // Run UI Updates
    }
}
Suragch
fonte