iPhone - Tópico principal do Grand Central Dispatch

145

Eu tenho usado com êxito o envio central grande em meus aplicativos, mas fiquei pensando qual é a real vantagem de usar algo como isto:

dispatch_async(dispatch_get_main_queue(), ^{ ... do stuff

ou mesmo

dispatch_sync(dispatch_get_main_queue(), ^{ ... do stuff

Quero dizer, nos dois casos, você está disparando um bloco para ser executado no encadeamento principal, exatamente onde o aplicativo é executado e isso não ajuda a reduzir a carga. No primeiro caso, você não tem controle quando o bloco será executado. Vi casos de blocos sendo executados meio segundo depois que você os disparou. O segundo caso, é semelhante ao

[self doStuff];

certo?

Eu me pergunto o que vocês acham.

Pato
fonte
9
A propósito, lançar uma fila principal em um dispatch_sync resultará em um impasse.
Brooks Hanes
5
Basta ler na documentação: "Ao contrário de dispatch_async, [dispatch_sync] não retorna até que o bloco termine. Chamar esta função e direcionar a fila atual resulta em impasse." ... Mas talvez eu esteja lendo errado ... ( a fila atual não significa thread principal). Corrija se eu estiver errado.
Brooks Hanes
4
@BrooksHanes nem sempre é verdade. Isso resultará em um impasse se você já estiver no segmento principal. Caso contrário, não haveria um impasse. Veja aqui
Honey

Respostas:

296

O envio de um bloco para a fila principal geralmente é feito a partir de uma fila em segundo plano para sinalizar que algum processamento em segundo plano foi concluído, por exemplo

- (void)doCalculation
{
    //you can use any string instead "com.mycompany.myqueue"
    dispatch_queue_t backgroundQueue = dispatch_queue_create("com.mycompany.myqueue", 0);

    dispatch_async(backgroundQueue, ^{
        int result = <some really long calculation that takes seconds to complete>;

        dispatch_async(dispatch_get_main_queue(), ^{
            [self updateMyUIWithResult:result];
        });    
    });
}

Nesse caso, estamos fazendo um cálculo demorado em uma fila de segundo plano e precisamos atualizar nossa interface do usuário quando o cálculo estiver concluído. A atualização da interface do usuário normalmente precisa ser feita a partir da fila principal, para que possamos 'sinalizar' de volta para a fila principal usando um segundo dispatch_async aninhado.

Provavelmente existem outros exemplos em que você pode enviar novamente para a fila principal, mas geralmente é feito dessa maneira, ou seja, aninhado de dentro de um bloco enviado para uma fila em segundo plano.

  • processamento em segundo plano concluído -> atualizar interface do usuário
  • parte dos dados processados ​​na fila de segundo plano -> fila principal de sinal para iniciar a próxima parte
  • dados de rede recebidos na fila de segundo plano -> fila principal de sinal de que a mensagem chegou
  • etc etc

Quanto ao motivo pelo qual você pode despachar para a fila principal a partir da fila principal ... Bem, geralmente você não faria isso, embora seja concebível que você possa fazê-lo para agendar algum trabalho para a próxima vez no ciclo de execução.

Robin Summerhill
fonte
Ah entendo. Então, eu estou certo. Não há vantagem em fazer isso se você já estiver na fila principal, apenas se estiver em outra fila e quiser atualizar a interface do usuário. Obrigado.
Duck
Acabei de editar minha resposta para falar sobre por que não é muito útil fazer isso na fila principal.
Robin Summerhill
Além disso, acho que há um bug no iOS 4 (pode ter sido no iOS 5), em que dispatch_sync para a fila principal do thread principal causa apenas um travamento, então eu evitaria isso completamente.
31411 joelick
10
Isso não é um bug, é o comportamento esperado. Comportamento não muito útil, reconhecidamente, mas você sempre precisa estar ciente de conflitos ao usar o dispatch_sync. Você não pode esperar que o sistema o proteja contra erros do programador o tempo todo.
Robin Summerhill
2
O que é backgroundQueue aqui? Como faço para criar backgroundQueue objeto
Nilesh Tupe
16

O envio de blocos para a fila principal a partir do encadeamento principal pode ser útil. Isso dá à fila principal a chance de lidar com outros blocos que foram enfileirados, para que você não esteja simplesmente impedindo a execução de todo o resto.

Por exemplo, você pode escrever um servidor encadeado essencialmente único que, no entanto, lida com muitas conexões simultâneas. Enquanto nenhum bloco individual na fila demorar muito, o servidor permanecerá responsivo a novas solicitações.

Se o seu programa não faz nada além de passar a vida inteira respondendo aos eventos, isso pode ser bastante natural. Você acabou de configurar seus manipuladores de eventos para serem executados na fila principal e depois chamar dispatch_main (), e talvez você não precise se preocupar com a segurança dos threads.

bames53
fonte
11

Espero que esteja entendendo sua pergunta corretamente, pois você está se perguntando sobre as diferenças entre dispatch_async e dispatch_sync?

dispatch_async

enviará o bloco para uma fila de forma assíncrona. Isso significa que ele enviará o bloco para a fila e não esperará que ele retorne antes de continuar a execução do código restante no seu método.

dispatch_sync

enviará o bloco para uma fila de forma síncrona. Isso impedirá qualquer execução do código restante no método até que o bloco termine de executar.

Utilizei principalmente dispatch_asynca fila de segundo plano para tirar o trabalho da fila principal e aproveitar os núcleos extras que o dispositivo possa ter. Em seguida, dispatch_asyncpara o segmento principal, se eu precisar atualizar a interface do usuário.

Boa sorte

timthetoolman
fonte
1
obrigado, mas estou perguntando sobre as vantagens de enviar algo para a fila principal, estar na fila principal.
Duck
9

Um local em que é útil é para atividades da interface do usuário, como definir um controle giratório antes de uma operação demorada:

- (void) handleDoSomethingButton{

    [mySpinner startAnimating];

    (do something lengthy)
    [mySpinner stopAnimating];
}

não funcionará, porque você está bloqueando o encadeamento principal durante o processo longo e não deixando o UIKit realmente iniciar o controle giratório.

- (void) handleDoSomethingButton{
     [mySpinner startAnimating];

     dispatch_async (dispatch_get_main_queue(), ^{
          (do something lengthy)
          [mySpinner stopAnimating];
    });
}

retornará o controle ao loop de execução, que agendará a atualização da interface do usuário, iniciando o controle giratório e retirará o próximo passo da fila de expedição, que é o seu processamento real. Quando o processamento é concluído, a parada de animação é chamada e você retorna ao loop de execução, onde a interface do usuário é atualizada com a parada.

weaselfloss1
fonte
@Jerceratops sim, mas permite que o runloop atual seja concluído.
21815 Dan Rosenstark
3
Sim, mas ainda é terrível. Ele ainda bloqueia a interface do usuário. Eu posso pressionar outro botão logo após este. Ou tente rolar. "(faça algo demorado)" não deve acontecer no thread principal, e dispatch_async para deixar o botão clicar em "concluir" não é uma solução aceitável.
Jerceratops
8

Swift 3, 4 e 5

Executando código no thread principal

DispatchQueue.main.async {
    // Your code here
}
Niall Kiddle
fonte
1

Assíncrono significa assíncrono e você deve usá-lo na maioria das vezes. Você nunca deve chamar a sincronização no thread principal, pois ele bloqueará sua interface do usuário até que a tarefa seja concluída. Aqui está uma maneira melhor de fazer isso no Swift:

runThisInMainThread { () -> Void in
    // Run your code like this:
    self.doStuff()
}

func runThisInMainThread(block: dispatch_block_t) {
    dispatch_async(dispatch_get_main_queue(), block)
}

Está incluído como uma função padrão no meu repositório, confira: https://github.com/goktugyil/EZSwiftExtensions

Esqarrouth
fonte