Diferença entre dispatch_async e dispatch_sync na fila serial?


125

Eu criei uma fila serial como esta:

    dispatch_queue_t _serialQueue = dispatch_queue_create("com.example.name", DISPATCH_QUEUE_SERIAL);

Qual é a diferença entre dispatch_asyncchamado assim

 dispatch_async(_serialQueue, ^{ /* TASK 1 */ });
 dispatch_async(_serialQueue, ^{ /* TASK 2 */ });

E dispatch_syncchamou assim nesta fila de série?

 dispatch_sync(_serialQueue, ^{ /* TASK 1 */ });
 dispatch_sync(_serialQueue, ^{ /* TASK 2 */ });

Meu entendimento é que, independentemente de qual método de despacho é usado, TASK 1será executado e concluído antes TASK 2, correto?

Respostas:


409

Sim. O uso da fila serial garante a execução serial de tarefas. A única diferença é que dispatch_syncsomente retorna após o término do bloco, enquanto o dispatch_asyncretorno após ser adicionado à fila e pode não ser concluído.

para este código

dispatch_async(_serialQueue, ^{ printf("1"); });
printf("2");
dispatch_async(_serialQueue, ^{ printf("3"); });
printf("4");

Pode imprimir 2413ou 2143ou 1234, mas 1sempre antes3

para este código

dispatch_sync(_serialQueue, ^{ printf("1"); });
printf("2");
dispatch_sync(_serialQueue, ^{ printf("3"); });
printf("4");

sempre imprime 1234


Nota: Para o primeiro código, ele não será impresso 1324. Porque printf("3")é despachado depois que printf("2") é executado. E uma tarefa só pode ser executada após ser despachada.


O tempo de execução das tarefas não muda nada. Esse código sempre imprime12

dispatch_async(_serialQueue, ^{ sleep(1000);printf("1"); });
dispatch_async(_serialQueue, ^{ printf("2"); });

O que pode acontecer é

  • Tópico 1: dispatch_async uma tarefa demorada (tarefa 1) para a fila serial
  • Tópico 2: comece a executar a tarefa 1
  • Tópico 1: dispatch_async outra tarefa (tarefa 2) na fila serial
  • Tópico 2: tarefa 1 concluída. comece a executar a tarefa 2
  • Tópico 2: tarefa 2 concluída.

e você sempre vê 12


7
também pode imprimir 2134 e 1243
Matteo Gobbi 22/03

minha pergunta é por que não fizemos isso da maneira normal? printf("1");printf("2") ;printf("3") ;printf("4")- comparado comdispatch_sync
androniennn

@androniennn para o segundo exemplo? porque outro segmento pode estar em execução dispatch_sync(_serialQueue, ^{ /*change shared data*/ });ao mesmo tempo.
Bryan Chen

1
@ asma22 É muito útil compartilhar um objeto seguro sem thread entre várias threads / filas de despacho. Se você acessar apenas o objeto em uma fila serial, saberá que está acessando com segurança.
Bryan Chen

1
Quero dizer execução em série . Do ponto de vista de que todas as tarefas são executadas, em relação a outras tarefas na mesma fila. De causa, ainda pode ser uma consideração simultânea para outras filas. É o ponto principal do GCD que tarefas podem ser despachadas e executadas simultaneamente.
Bryan Chen

19

A diferença entre dispatch_synce dispatch_asyncé simples.

Nos dois exemplos, TASK 1sempre será executado antes TASK 2porque foi despachado antes dele.

No dispatch_syncexemplo, no entanto, você não enviará TASK 2até que depois TASK 1seja despachado e executado . Isso é chamado de "bloqueio" . Seu código aguarda (ou "bloqueia") até que a tarefa seja executada.

No dispatch_asyncexemplo, seu código não aguardará a conclusão da execução. Ambos os blocos serão despachados (e serão enfileirados) para a fila e o restante do seu código continuará sendo executado nesse segmento. Então, em algum momento no futuro (dependendo do que mais foi despachado para sua fila), Task 1será executado e, em seguida, Task 2será executado.


2
Eu acho que você está errado. primeiro exemplo é asynco que é a versão de não-bloqueio
Bryan Chen

Editei sua resposta para o que acho que você quis dizer . Se não for esse o caso, altere-o e esclareça.
JRG-Developer

1
E se você chamar dispatch_sync e, em seguida, dispatch_async na mesma fila? (e vice-versa)
0xSina 29/11

1
Em uma fila serial, as duas tarefas ainda são executadas uma após a outra. No primeiro caso, o chamador aguarda a conclusão do primeiro bloco, mas não espera o segundo bloco. No segundo caso, o chamador não espera o primeiro bloco terminar, mas espera o segundo bloco. Mas como a fila executa os blocos em ordem, o chamador espera efetivamente que ambos terminem.
gnasher729

1
Um bloco também pode fazer um dispatch_async em sua própria fila (adicionando outros blocos que serão executados posteriormente); dispatch_sync na própria fila serial ou na fila principal entraria em conflito. Nessa situação, o chamador aguardará o término do bloco original, mas não os outros blocos. Lembre-se: o dispatch_sync coloca o bloco no final da fila, a fila executa o código até o final do bloco e, em seguida, o dispatch_sync retorna. dispatch_async apenas adiciona o bloco no final da fila.
gnasher729

5

Está tudo relacionado à fila principal. Existem 4 permutações.

i) Fila serial, expedição assíncrona: aqui as tarefas serão executadas uma após a outra, mas o encadeamento principal (efeito na interface do usuário) não esperará o retorno

ii) Fila serial, sincronização de despacho: aqui as tarefas serão executadas uma após a outra, mas o encadeamento principal (efeito na interface do usuário) mostrará atraso

iii) Fila simultânea, expedição assíncrona: aqui as tarefas serão executadas em paralelo e o encadeamento principal (efeito na interface do usuário) não esperará pelo retorno e será tranquilo.

iv) Fila simultânea, sincronização de despacho: Aqui as tarefas serão executadas em paralelo, mas o encadeamento principal (efeito na interface do usuário) mostrará atraso

Sua escolha da fila simultânea ou serial depende se você precisa de uma saída de uma tarefa anterior para a próxima. Se você depende da tarefa anterior, adote a fila serial ou faça a fila simultânea.

E, finalmente, essa é uma maneira de penetrar novamente no segmento principal quando terminarmos nossos negócios:

DispatchQueue.main.async {
     // Do something here
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.