GCD para realizar la tarea en el hilo principal

254

Tengo una devolución de llamada que podría provenir de cualquier hilo. Cuando recibo esta devolución de llamada, me gustaría realizar una determinada tarea en el hilo principal.

¿Debo verificar si ya estoy en el hilo principal, o hay alguna penalización por no realizar esta verificación antes de llamar al código a continuación?

dispatch_async(dispatch_get_main_queue(), ^{
   // do work here
});
Egil
fuente
116
Cinco años después, todavía no recuerdo la sintaxis de los bloques GCD y termino aquí cada vez.
SpaceTrucker
77
@SpaceTrucker - Esa es la misma razón por la que estoy en esta página: D
Matthew Cawley
44
9 años después, y todavía vengo a copiar la sintaxis de esta página.
Osa

Respuestas:

152

No, no necesita verificar si ya está en el hilo principal. Al enviar el bloque a la cola principal, solo está programando que el bloque se ejecute en serie en el hilo principal, lo que sucede cuando se ejecuta el ciclo de ejecución correspondiente.

Si ya está en el hilo principal, el comportamiento es el mismo: el bloque está programado y se ejecuta cuando se ejecuta el bucle de ejecución del hilo principal.


fuente
3
La pregunta era si hay una "penalización por no realizar esta verificación" ... Creo que hay una penalización de rendimiento por usar el envío asíncrono cuando no es necesario, ¿o es trivial?
Dan Rosenstark
1
@Yar No creo que haya un impacto notable en el rendimiento en la mayoría de los casos: GCD es una biblioteca ligera. Dicho esto, entendí la pregunta como: 'dado el código a continuación, ¿debo verificar si estoy en el hilo principal?'
77
Sin embargo, debe verificar si utiliza dispatch_sync. De lo contrario, tienes un punto muerto.
Victor Engel
Si está en la cola principal y asyncregresa a la cola principal, se ejecutará, pero eso puede estropear el tiempo esperado de sus acciones . Por ejemplo, el código de la IU viewDidLoad() no se ejecuta hasta después de que se muestra la vista por primera vez .
pkamb
106

Para el caso de despacho asíncrono que describió anteriormente, no debería necesitar verificar si está en el hilo principal. Como indica Bavarious, esto simplemente se pondrá en cola para ejecutarse en el hilo principal.

Sin embargo, si intenta hacer lo anterior usando dispatch_sync()ay su devolución de llamada está en el hilo principal, su aplicación se bloqueará en ese punto. Describo esto en mi respuesta aquí , porque este comportamiento me sorprendió al mover algo de código -performSelectorOnMainThread:. Como mencioné allí, creé una función auxiliar:

void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_sync(dispatch_get_main_queue(), block);
    }
}

que ejecutará un bloque sincrónicamente en el hilo principal si el método en el que se encuentra no está actualmente en el hilo principal, y simplemente ejecuta el bloque en línea si lo es. Puede utilizar una sintaxis como la siguiente para usar esto:

runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});
Brad Larson
fuente
1
¿Por qué no llamarlo algo así como "runOnMainQueueSync"? El hecho de que podría llegar a un punto muerto y no lo es es el tipo de cosas que no quisiera tener en todo mi código. Gracias y +1 como siempre.
Dan Rosenstark
2
@Yar: acabo de darle ese nombre para que me quede claro por qué no estaba simplemente disparando bloques sincrónicamente en la cola principal en lugar de usar esta función auxiliar. Fue más un recordatorio para mí mismo.
Brad Larson
3
Todavía es posible llegar a un punto muerto con esta función. Sincronización de la cola principal> sincronización de otra cola> la cola principal se bloqueará.
hfossli
2
@hfossli - Cierto, no manejará todos los casos. En mi uso, siempre estaba llamando desde un despacho asíncrono en una cola serial en segundo plano o código en línea en el hilo principal. Me pregunto si dispatch_set_specific()ayudaría en el caso que describa: stackoverflow.com/a/12806754/19679 .
Brad Larson
1
[NSThread isMainThread] a menudo devuelve SÍ y no se considera seguro para verificar este caso en la programación GCD. stackoverflow.com/questions/14716334/…
Will Larche
57

Como se menciona en las otras respuestas, dispatch_async desde el hilo principal está bien.

Sin embargo, dependiendo de su caso de uso, hay un efecto secundario que puede considerar una desventaja: dado que el bloque está programado en una cola, no se ejecutará hasta que el control regrese al ciclo de ejecución, lo que tendrá el efecto de retrasarse La ejecución de tu bloque.

Por ejemplo,

NSLog(@"before dispatch async");
dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"inside dispatch async block main thread from main thread");
});
NSLog(@"after dispatch async");

Imprimirá:

before dispatch async
after dispatch async
inside dispatch async block main thread from main thread

Por esta razón, si esperaba que el bloque se ejecute entre los NSLog externos, dispatch_async no lo ayudaría.

Michael Chinen
fuente
Debe decir que esto es algo importante a tener en cuenta
Marc-Alexandre Bérubé
Como desea verificar, esto significa que el código puede o no ejecutarse en el hilo principal. Ya en el hilo principal se ejecutará en ese orden, de lo contrario, esto no se puede garantizar. Por lo tanto, debe evitar diseñar el código para que se ejecute en un orden específico cuando se refiera a la programación multiproceso. Intente utilizar la forma de devolución de llamada asíncrona.
Ups
1

No, no necesita verificar si está en el hilo principal. Así es como puedes hacer esto en Swift:

runThisInMainThread { () -> Void in
    runThisInMainThread { () -> Void in
        // No problem
    }
}

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