59

Necesito enviar un bloque en la cola principal, sincrónicamente. No sé si actualmente estoy ejecutando el hilo principal o no. La solución ingenua se ve así:¿Cómo despachar en la cola principal sincrónicamente sin un punto muerto?

dispatch_sync(dispatch_get_main_queue(), block); 

Pero si estoy actualmente en el interior de un bloque que se ejecuta en la cola principal, esta llamada crea un punto muerto. (El envío síncrono espera a que el bloque hasta el final, pero el bloque ni siquiera empezar a correr, ya que estamos esperando a que el actual para terminar.)

El siguiente paso obvio es para comprobar si la cola actual:

if (dispatch_get_current_queue() == dispatch_get_main_queue()) { 
    block(); 
} else { 
    dispatch_sync(dispatch_get_main_queue(), block); 
} 

Esto funciona, pero es feo. Antes de que al menos lo oculte detrás de alguna función personalizada, ¿no hay una mejor solución para este problema? Insisto en que no puedo permitirme el envío del bloque de forma asíncrona: la aplicación se encuentra en una situación en la que el bloque enviado de forma asíncrona se ejecutará "demasiado tarde".

+0

Creo que es bastante buena solución. Todo lo que puede hacer es hacerlo como una macro predefinida, por lo que su código no se verá tan feo. –

+1

Esta es más o menos la solución de "libro de texto". –

Respuesta

63

tengo que usar algo como esto con bastante regularidad dentro de mis aplicaciones de Mac y iOS, así que usa la siguiente función auxiliar (descrito originalmente en this answer):

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

la que se llama a través de

runOnMainQueueWithoutDeadlocking(^{ 
    //Do stuff 
}); 

Este es prácticamente el proceso que describes arriba, y he hablado con muchos otros desarrolladores que independientemente han creado algo como esto por sí mismos.

que utilizan [NSThread isMainThread] en vez de comprobar dispatch_get_current_queue(), debido a que el caveats section for that function vez advirtió contra el uso de esta para las pruebas de identidad y the call was deprecated in iOS 6.

+0

Gran respuesta, también 'dispatch_get_current_queue()' está en desuso ahora – txulu

+1

@Brad_Larson ¿existe alguna vez una situación real en la que un desarrollador que controle su código no sepa si se está derivando del hilo principal o no? Mi creencia es que si uno sabe cómo está funcionando su código, entonces sabrá qué estaba/está/estará en el hilo principal o de lo contrario. – pnizzle

+2

@pnizzle - Sí, hay muchos casos en que esto puede ocurrir.Si tiene un método común en el que se realizan operaciones que deben ejecutarse en el hilo principal, pero que se llama desde muchos lugares, algunos en el hilo principal, otros no, una función como esta es extremadamente útil. Escribí esto porque lo necesitaba en varios lugares yo mismo. –

0

Recientemente comencé a experimentar un punto muerto durante las actualizaciones de IU. Eso me llevó a esta pregunta de Desbordamiento de pila, lo que me llevó a implementar una función de ayuda tipo runOnMainQueueWithoutDeadlocking basada en la respuesta aceptada.

El problema real, sin embargo, es que al actualizar la interfaz de usuario de un bloque que había utilizado por error dispatch_sync en lugar de dispatch_async para obtener la cola principal para las actualizaciones de la interfaz de usuario. Fácil de hacer con la finalización del código, y tal vez difícil de notar después del hecho.

Por lo tanto, para otros la lectura de esta pregunta: si ejecución sincrónica no se requiere, el simple uso de dispatch_**a**sync evitará el estancamiento que puede ser intermitente golpear.

1

para la sincronización en la cola principal o en el hilo principal (que no es lo mismo) que utilizo:

import Foundation 

private let mainQueueKey = UnsafeMutablePointer<Void>.alloc(1) 
private let mainQueueValue = UnsafeMutablePointer<Void>.alloc(1) 


public func dispatch_sync_on_main_queue(block:() -> Void) 
{ 
    struct dispatchonce { static var token : dispatch_once_t = 0 } 
    dispatch_once(&dispatchonce.token, 
    { 
     dispatch_queue_set_specific(dispatch_get_main_queue(), mainQueueKey, mainQueueValue, nil) 
    }) 

    if dispatch_get_specific(mainQueueKey) == mainQueueValue 
    { 
     block() 
    } 
    else 
    { 
     dispatch_sync(dispatch_get_main_queue(),block) 
    } 
} 

extension NSThread 
{ 
    public class func runBlockOnMainThread(block:() -> Void) 
    { 
     if NSThread.isMainThread() 
     { 
      block() 
     } 
     else 
     { 
      dispatch_sync(dispatch_get_main_queue(),block) 
     } 
    } 

    public class func runBlockOnMainQueue(block:() -> Void) 
    { 
     dispatch_sync_on_main_queue(block) 
    } 
} 
Cuestiones relacionadas