iPhone: hilo principal de Grand Central Dispatch

145

He estado usando con éxito, gran distribución central en mis aplicaciones, pero me preguntaba cuál es la verdadera ventaja de usar algo como esto:

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

o incluso

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

Quiero decir, en ambos casos estás disparando un bloque para que se ejecute en el hilo principal, exactamente donde se ejecuta la aplicación y esto no ayudará a reducir la carga. En el primer caso, no tienes ningún control sobre cuándo se ejecutará el bloque. He visto casos de bloques ejecutados medio segundo después de que los disparas. El segundo caso, es similar a

[self doStuff];

¿Derecha?

Me pregunto qué piensan ustedes.

Pato
fuente
9
Por cierto, lanzar una cola principal en un dispatch_sync dará como resultado un punto muerto.
Brooks Hanes
55
Simplemente léalo en los documentos: "A diferencia de dispatch_async, [dispatch_sync] no regresa hasta que el bloque haya finalizado. Llamar a esta función y apuntar a la cola actual da como resultado un punto muerto" ... Pero tal vez estoy leyendo esto mal ... ( la cola actual no significa hilo principal). Por favor corrija si estoy equivocado.
Brooks Hanes
44
@BrooksHanes no siempre es cierto. Resultará un punto muerto si ya está en el hilo principal. Si no, entonces no habría un punto muerto. Ver aquí
Miel

Respuestas:

296

El envío de un bloque a la cola principal generalmente se realiza desde una cola en segundo plano para indicar que ha finalizado algún procesamiento en segundo plano, por ejemplo

- (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];
        });    
    });
}

En este caso, estamos haciendo un cálculo largo en una cola en segundo plano y necesitamos actualizar nuestra interfaz de usuario cuando se complete el cálculo. La actualización de la interfaz de usuario normalmente debe realizarse desde la cola principal, por lo que 'señalizamos' de nuevo a la cola principal utilizando un segundo dispatch_async anidado.

Probablemente hay otros ejemplos en los que es posible que desee volver a la cola principal, pero generalmente se hace de esta manera, es decir, anidada desde un bloque enviado a una cola en segundo plano.

  • procesamiento en segundo plano terminado -> actualizar la interfaz de usuario
  • fragmento de datos procesados ​​en la cola de fondo -> señalizar la cola principal para comenzar el siguiente fragmento
  • datos de red entrantes en la cola de fondo -> señalizar la cola principal de que el mensaje ha llegado
  • etcétera etcétera

En cuanto a por qué es posible que desee enviar a la cola principal desde la cola principal ... Bueno, generalmente no lo haría, aunque posiblemente podría hacerlo para programar algún trabajo para la próxima vez en el ciclo de ejecución.

Robin Summerhill
fuente
Ah, ya veo. Entonces tengo razón. No tiene ninguna ventaja hacerlo si ya está en la cola principal, solo si está en otra cola y desea actualizar la IU. Gracias.
Pato
Acabo de editar mi respuesta para hablar sobre por qué no es muy útil hacer esto desde la cola principal.
Robin Summerhill
Además, creo que hay un error en iOS 4 (podría haber ido en iOS 5), donde dispatch_sync a la cola principal desde el hilo principal solo causa un bloqueo, por lo que evitaría hacerlo por completo.
joerick
10
Eso no es un error, ese es el comportamiento esperado. Es cierto que no es un comportamiento muy útil, pero siempre debe tener en cuenta los puntos muertos cuando se utiliza dispatch_sync. No puede esperar que el sistema lo proteja del error del programador todo el tiempo.
Robin Summerhill
2
¿Qué es backgroundQueue aquí? ¿Cómo creo un objeto backgroundQueue?
Nilesh Tupe
16

El envío de bloques a la cola principal desde el hilo principal puede ser útil. Le da a la cola principal la oportunidad de manejar otros bloques que se han puesto en cola para que no esté simplemente bloqueando la ejecución de todo lo demás.

Por ejemplo, podría escribir un servidor de subprocesos esencialmente único que, sin embargo, maneje muchas conexiones concurrentes. Mientras ningún bloque individual en la cola tarde demasiado, el servidor responde a las nuevas solicitudes.

Si su programa no hace nada más que pasar toda su vida respondiendo a eventos, entonces esto puede ser bastante natural. Simplemente configure sus controladores de eventos para que se ejecuten en la cola principal y luego llame a dispatch_main (), y es posible que no tenga que preocuparse por la seguridad del hilo.

bames53
fuente
11

Espero que comprenda su pregunta correctamente, ya que se pregunta sobre las diferencias entre dispatch_async y dispatch_sync.

dispatch_async

enviará el bloque a una cola de forma asincrónica. Lo que significa que enviará el bloque a la cola y no esperará a que regrese antes de continuar con la ejecución del código restante en su método.

dispatch_sync

enviará el bloque a una cola sincrónicamente. Esto evitará más ejecución del código restante en el método hasta que el bloque haya terminado de ejecutarse.

Principalmente utilicé una dispatch_asynccola de fondo para sacar el trabajo de la cola principal y aprovechar los núcleos adicionales que pueda tener el dispositivo. Luego dispatch_asyncal hilo principal si necesito actualizar la interfaz de usuario.

Buena suerte

timthetoolman
fuente
1
gracias, pero estoy preguntando sobre las ventajas de enviar algo a la cola principal, estar en la cola principal.
Pato
9

Un lugar donde es útil es para las actividades de la interfaz de usuario, como configurar una ruleta antes de una operación larga:

- (void) handleDoSomethingButton{

    [mySpinner startAnimating];

    (do something lengthy)
    [mySpinner stopAnimating];
}

no funcionará, ya que está bloqueando el hilo principal durante su actividad prolongada y no está permitiendo que UIKit realmente inicie la ruleta.

- (void) handleDoSomethingButton{
     [mySpinner startAnimating];

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

devolverá el control al ciclo de ejecución, que programará la actualización de la interfaz de usuario, iniciará el control giratorio, luego obtendrá lo siguiente de la cola de despacho, que es su procesamiento real. Cuando finaliza el procesamiento, se llama a la parada de animación y vuelve al ciclo de ejecución, donde la interfaz de usuario se actualiza con la parada.

weaselfloss1
fuente
@Jerceratops sí, pero permite que se complete el runloop actual.
Dan Rosenstark
3
Sí, pero sigue siendo terrible. Todavía bloquea la interfaz de usuario. Podría presionar otro botón justo después de este. O intenta desplazarte. "(hacer algo largo)" no debería suceder en el hilo principal, y dispatch_async para dejar que el botón haga clic en "finalizar" no es una solución aceptable.
Jerceratops
8

Swift 3, 4 y 5

Ejecutando código en el hilo principal

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

Asíncrono significa asíncrono y debes usarlo la mayor parte del tiempo. Nunca debe llamar a la sincronización en el hilo principal porque bloqueará su interfaz de usuario hasta que se complete la tarea. You Here es una mejor manera de hacer esto en 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á incluido como una función estándar en mi repositorio, échale un vistazo: https://github.com/goktugyil/EZSwiftExtensions

Esqarrouth
fuente