Comprensión de dispatch_async

233

Tengo una pregunta sobre este 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];
});

El primer parámetro de este código es

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 

¿Le estamos pidiendo a este código que realice tareas en serie en la cola global cuya definición en sí misma es que devuelve una cola concurrente global de un nivel de prioridad dado?

¿Cuál es la ventaja de usar dispatch_get_global_queuesobre la cola principal?

Estoy confundido. ¿Podrías ayudarme a entender esto mejor?

usuario2332873
fuente
1
Debería cortar mejor su código en varias líneas para que tenga más sentido. seguro su dispatch_get_global_queuedentro de un tipo variable de dispatch_queue_t myQueue. Es más legible pasar solo myQueue a su `` dispatch_async`
Alex Cio

Respuestas:

517

La razón principal por la que usa la cola predeterminada sobre la cola principal es para ejecutar tareas en segundo plano.

Por ejemplo, si estoy descargando un archivo de Internet y quiero actualizar al usuario sobre el progreso de la descarga, ejecutaré la descarga en la cola predeterminada de prioridad y actualizaré la IU en la cola principal de forma asincrónica.

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
fuente
Entiendo que David agradece su respuesta, pero mi pregunta fue más comprensible para entender la lógica de hacer esto, es decir, le pido a este código que realice tareas en serie en la cola global, que es la cola concurrente en sí misma
user2332873
Estoy haciendo exactamente lo que usted sugiere, pero de alguna manera, uiTableViewCell no se actualiza de inmediato cuando llamo a [self.tableView reloadData] en las actualizaciones de la interfaz de usuario de ejecución. Tarda unos 4 o 5 segundos. Me ha estado volviendo loco durante varios días. .
GrandSteph
@GrandSteph No estoy muy familiarizado con ese método. Tal vez ese método solo tome 5 segundos para ejecutarse. Lo importante con dispatch_async es que te permite hacer cosas en segundo plano sin colgar el hilo principal.
David
2
¿Qué significa el 0significado?
Miel
3
@Honey El 0 es el flagsparámetro, que actualmente no hace absolutamente nada. De los documentos:Flags that are reserved for future use. Always specify 0 for this parameter.
David
199

Todas las colas DISPATCH_QUEUE_PRIORITY_X son colas concurrentes (lo que significa que pueden ejecutar múltiples tareas a la vez), y son FIFO en el sentido de que las tareas dentro de una cola dada comenzarán a ejecutarse usando el orden "primero en entrar, primero en salir". Esto es en comparación con la cola principal (de dispatch_get_main_queue ()), que es una cola en serie (las tareas comenzarán a ejecutarse y terminarán de ejecutarse en el orden en que se reciben).

Entonces, si envía 1000 bloques dispatch_async () a DISPATCH_QUEUE_PRIORITY_DEFAULT, esas tareas comenzarán a ejecutarse en el orden en que las envió a la cola. Del mismo modo para las colas HIGH, LOW y BACKGROUND. Todo lo que envíe a cualquiera de estas colas se ejecuta en segundo plano en subprocesos alternativos, lejos de su subproceso principal de la aplicación. Por lo tanto, estas colas son adecuadas para ejecutar tareas como la descarga en segundo plano, la compresión, el cálculo, etc.

Tenga en cuenta que el orden de ejecución es FIFO por cola. Entonces, si envía 1000 tareas de dispatch_async () a las cuatro colas concurrentes diferentes, dividiéndolas de manera uniforme y enviándolas a BACKGROUND, LOW, DEFAULT y HIGH en orden (es decir, programa las últimas 250 tareas en la cola HIGH), es muy probable que las primeras tareas que veas comenzar estarán en esa fila ALTA ya que el sistema ha asumido que esas tareas deben llegar a la CPU lo más rápido posible.

Tenga en cuenta también que digo "comenzará a ejecutarse en orden", pero tenga en cuenta que, como colas concurrentes, las cosas no necesariamente TERMINARÁN la ejecución en orden, dependiendo del período de tiempo para cada tarea.

Según Apple:

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

Una cola de despacho concurrente es útil cuando tiene múltiples tareas que pueden ejecutarse en paralelo. Una cola concurrente sigue siendo una cola, ya que elimina las tareas en un orden de primero en entrar, primero en salir; sin embargo, una cola concurrente puede eliminar tareas adicionales antes de que finalicen las tareas anteriores. El número real de tareas ejecutadas por una cola concurrente en cualquier momento es variable y puede cambiar dinámicamente a medida que cambian las condiciones en su aplicación. Muchos factores afectan el número de tareas ejecutadas por las colas concurrentes, incluido el número de núcleos disponibles, la cantidad de trabajo realizado por otros procesos y el número y la prioridad de las tareas en otras colas de despacho en serie.

Básicamente, si envía esos 1000 bloques dispatch_async () a una cola DEFAULT, HIGH, LOW o BACKGROUND, todos comenzarán a ejecutarse en el orden en que los envía. Sin embargo, las tareas más cortas pueden terminar antes que las más largas. Las razones detrás de esto son si hay núcleos de CPU disponibles o si las tareas de la cola actual están realizando un trabajo computacionalmente no intensivo (haciendo que el sistema piense que puede despachar tareas adicionales en paralelo independientemente del conteo de núcleos).

El nivel de concurrencia es manejado completamente por el sistema y se basa en la carga del sistema y otros factores determinados internamente. Esta es la belleza de Grand Central Dispatch (el sistema dispatch_async ()): solo hace que sus unidades de trabajo sean bloques de código, establezca una prioridad para ellas (según la cola que elija) y deje que el sistema se encargue del resto.

Entonces, para responder a la pregunta anterior: estás parcialmente correcto. Está "solicitando a ese código" que realice tareas concurrentes en una cola global concurrente con el nivel de prioridad especificado. El código en el bloque se ejecutará en segundo plano y cualquier código adicional (similar) se ejecutará potencialmente en paralelo dependiendo de la evaluación del sistema de los recursos disponibles.

La cola "principal" por otro lado (de dispatch_get_main_queue ()) es una cola en serie (no concurrente). Las tareas enviadas a la cola principal siempre se ejecutarán en orden y siempre terminarán en orden. Estas tareas también se ejecutarán en el subproceso de la interfaz de usuario, por lo que es adecuado para actualizar su interfaz de usuario con mensajes de progreso, notificaciones de finalización, etc.

SimplePanda
fuente
+1, pero creo que en la práctica no importa mucho si las colas concurrentes son FIFO o simplemente orden aleatorio. Si comienza 5 tareas en un bucle, suponga que esencialmente comenzarán al mismo tiempo. No hay garantía de que, por ejemplo, la primera operación de E / S de la primera tarea ocurra antes de la quinta, incluso si ejecutan el mismo código. OTOH, para las colas en serie, el comportamiento FIFO es esencial y, en mi humilde opinión, esta es la diferencia definitoria entre los dos tipos de colas.
Gerhard Wesp
Increíble explicación. Aplaude mucho!
Okhan Okbay
36

Versión rápida

Esta es la versión Swift de la respuesta Objective-C de David. Utiliza la cola global para ejecutar cosas en segundo plano y la cola principal para actualizar la interfaz de usuario.

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