¿Diferencia entre dispatch_async y dispatch_sync en la cola serial?

125

He creado una cola en serie como esta:

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

¿Cuál es la diferencia entre dispatch_asyncllamado así

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

¿Y dispatch_syncllamado así en esta cola serial?

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

Tengo entendido que, independientemente del método de envío utilizado, TASK 1se ejecutará y completará antes TASK 2, ¿correcto?

JRG-Developer
fuente

Respuestas:

409

Si. El uso de la cola en serie garantiza la ejecución en serie de las tareas. La única diferencia es que dispatch_syncsolo regresa después de que el bloque ha finalizado, mientras que dispatch_asyncregresa después de que se agrega a la cola y puede que no finalice.

para este código

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

Puede imprimir 2413o 2143o 1234pero 1siempre antes3

para este código

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

siempre imprime 1234


Nota: Para el primer código, no se imprimirá 1324. Porque printf("3")se despacha después de que printf("2") se ejecuta. Y una tarea solo se puede ejecutar después de que se envíe.


El tiempo de ejecución de las tareas no cambia nada. Este código siempre se imprime12

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

Lo que puede pasar es

  • Hilo 1: dispatch_async una tarea que consume mucho tiempo (tarea 1) a la cola en serie
  • Hilo 2: comenzar a ejecutar la tarea 1
  • Hilo 1: dispatch_async otra tarea (tarea 2) a la cola serial
  • Hilo 2: tarea 1 finalizada. comenzar a ejecutar la tarea 2
  • Hilo 2: tarea 2 finalizada.

y siempre ves 12

Bryan Chen
fuente
77
también puede imprimir 2134 y 1243
Matteo Gobbi
mi pregunta es ¿por qué no lo hicimos de la manera normal? printf("1");printf("2") ;printf("3") ;printf("4")- en comparación condispatch_sync
androniennn
@androniennn para el segundo ejemplo? porque algún otro hilo puede ejecutarse dispatch_sync(_serialQueue, ^{ /*change shared data*/ });al mismo tiempo.
Bryan Chen
1
@ asma22 Es muy útil compartir un objeto no seguro para subprocesos entre múltiples subprocesos / colas de despacho. Si solo accede al objeto en una cola en serie, sabe que está accediendo a él de manera segura.
Bryan Chen
1
Me refiero a la ejecución en serie . En el punto de vista de que todas las tareas se ejecutan en serie con respecto a otras tareas en la misma cola. Por causa aún puede ser concurrente respecto a otras colas. El objetivo de GCD es que las tareas se puedan despachar y ejecutar simultáneamente.
Bryan Chen
19

La diferencia entre dispatch_syncy dispatch_asynces simple.

En ambos ejemplos, TASK 1siempre se ejecutará antes TASK 2porque se envió antes.

En el dispatch_syncejemplo, sin embargo, no enviará TASK 2hasta que TASK 1haya sido despachado y ejecutado . Esto se llama "bloqueo" . Su código espera (o "bloquea") hasta que se ejecuta la tarea.

En el dispatch_asyncejemplo, su código no esperará a que se complete la ejecución. Ambos bloques se enviarán (y se colocarán en cola) a la cola y el resto de su código continuará ejecutándose en ese hilo. Luego, en algún momento en el futuro, (dependiendo de qué más se haya enviado a su cola), Task 1se ejecutará y luego Task 2se ejecutará.

Dave DeLong
fuente
2
Creo que te equivocaste de orden. primer ejemplo es asynccuál es la versión sin bloqueo
Bryan Chen
He editado tu respuesta a lo que creo que querías decir . Si este no es el caso, cámbielo y aclare.
JRG-Developer
1
¿Qué sucede si llama a dispatch_sync y luego dispatch_async en la misma cola? (y viceversa)
0xSina
1
En una cola en serie, ambas tareas aún se ejecutan una tras otra. En el primer caso, la persona que llama espera a que termine el primer bloque pero no espera el segundo bloque. En el segundo caso, la persona que llama no espera a que termine el primer bloque, sino que espera el segundo bloque. Pero como la cola ejecuta los bloques en orden, la persona que llama efectivamente espera a que ambos terminen.
gnasher729
1
Un bloque también podría hacer un dispatch_async en su propia cola (agregando más bloques que se ejecutarán más adelante); dispatch_sync en la propia cola en serie o en la cola principal se estancaría. En esta situación, la persona que llama esperará a que termine el bloque original, pero no a los otros bloques. Solo recuerde: dispatch_sync coloca el bloque al final de la cola, la cola ejecuta el código hasta que finaliza ese bloque y luego dispatch_sync regresa. dispatch_async simplemente agrega el bloque al final de la cola.
gnasher729
5

Todo está relacionado con la cola principal. Hay 4 permutaciones.

i) Cola de serie, despacho asíncrono: aquí las tareas se ejecutarán una tras otra, pero el hilo principal (efecto en la interfaz de usuario) no esperará el regreso

ii) Cola de serie, sincronización de despacho: aquí las tareas se ejecutarán una tras otra, pero el hilo principal (efecto en la interfaz de usuario) mostrará un retraso

iii) Cola concurrente, despacho asíncrono: aquí las tareas se ejecutarán en paralelo y el hilo principal (efecto en la interfaz de usuario) no esperará el regreso y será fluido.

iv) Cola concurrente, sincronización de despacho: aquí las tareas se ejecutarán en paralelo, pero el hilo principal (efecto en la interfaz de usuario) mostrará un retraso

Su elección de la cola concurrente o en serie depende de si necesita un resultado de una tarea anterior para la siguiente. Si depende de la tarea anterior, adopte la cola en serie o tome cola concurrente.

Y, por último, esta es una forma de volver al hilo principal cuando hayamos terminado con nuestro negocio:

DispatchQueue.main.async {
     // Do something here
}
rd_
fuente