2011-10-19 13 views
36

Estaba teniendo problemas para probar algunas unidades de algún gran código central de despacho con el marco de prueba de unidad integrado de Xcode, SenTestingKit. Pude hervir mi problema hecho a esto. Tengo una prueba de unidad que construye un bloque e intenta ejecutarlo en el hilo principal. Sin embargo, el bloque nunca se ejecuta realmente, por lo que la prueba se bloquea porque es un envío síncrono.dispatch_sync en la cola principal se cuelga en la prueba de la unidad

- (void)testSample { 

    dispatch_sync(dispatch_get_main_queue(), ^(void) { 
     NSLog(@"on main thread!"); 
    }); 

    STFail(@"FAIL!"); 
} 

¿Qué hay en el ambiente de prueba que hace que esto se cuelgue?

+3

Buena pregunta y espero la respuesta correcta. He encontrado varias veces que el uso de dispatch_sync en la cola principal termina en un punto muerto, así que lo evito en general. –

+0

@ D.C. varias veces o SIEMPRE? Tengo curiosidad por saber cómo '' dispatch_sync (dispatch_get_main_queue() '** while ** en el hilo principal no creará dead lock !? – Honey

Respuesta

99

dispatch_sync ejecuta un bloque en una cola determinada y espera a que se complete. En este caso, la cola es la cola principal de envío. La cola principal ejecuta todas sus operaciones en el hilo principal, en orden FIFO (primero en entrar, primero en salir). Eso significa que siempre que llame al dispatch_sync, su nuevo bloque se colocará al final de la línea y no se ejecutará hasta que todo lo demás antes de que termine en la cola.

El problema aquí es que el bloque que acaba en cola se encuentra en el extremo de la línea de espera para ejecutar en el hilo principal, mientras que el método de testSamplees actualmente en ejecución en el hilo principal. El bloque al final de la cola no puede obtener acceso al hilo principal hasta que el método actual (sí mismo) finalice usando el hilo principal. Sin embargo, dispatch_sync significa Someter un objeto de bloque para su ejecución en una cola de distribución y espera hasta que ese bloque se complete.

+1

Excelente, gracias por la explicación detallada. – Drewsmits

+1

Encantador. En segundo lugar, gracias Tengo un código que quiero forzar para que se ejecute en el hilo principal, así que esta parece la manera de hacerlo. Pero (a) ¿cómo puedo saber si estoy en otro hilo y (b) cómo fue que mi no- el código principal se pone allí en primer lugar? Cualquier pista sería bienvenida :) –

+0

¿Sería esto un ejemplo de bloqueo? – Pescolly

7

El problema en su código es que no importa si utiliza dispatch_sync o dispatch_async, siempre se llamará a STFail(), lo que hará que la prueba falle.

Lo que es más importante, como explicó BJ Homer, si necesita ejecutar algo sincrónicamente en la cola principal, debe asegurarse de que no se encuentre en la cola principal o que se produzca un bloqueo. Si está en la cola principal, simplemente puede ejecutar el bloque como una función normal.

Espero que esto ayude:

- (void)testSample { 

    __block BOOL didRunBlock = NO; 
    void (^yourBlock)(void) = ^(void) { 
     NSLog(@"on main queue!"); 
     // Probably you want to do more checks here... 
     didRunBlock = YES; 
    }; 

    // 2012/12/05 Note: dispatch_get_current_queue() function has been 
    // deprecated starting in iOS6 and OSX10.8. Docs clearly state they 
    // should be used only for debugging/testing. Luckily this is our case :) 
    dispatch_queue_t currentQueue = dispatch_get_current_queue(); 
    dispatch_queue_t mainQueue = dispatch_get_main_queue(); 

    if (currentQueue == mainQueue) { 
     blockInTheMainThread(); 
    } else { 
     dispatch_sync(mainQueue, yourBlock); 
    } 

    STAssertEquals(YES, didRunBlock, @"FAIL!"); 
} 
6

Si usted está en la cola principal y sincrónicamente espere a la cola principal que esté disponible que de hecho esperar mucho tiempo. Debes probar para asegurarte de que aún no estás en el hilo principal.

2

durante el seguimiento, ya

dispatch_get_current_queue() 

ya no se utiliza, puede utilizar

[NSThread isMainThread] 

para ver si está en el hilo principal.

Por lo tanto, el uso de la otra respuesta anterior, se puede hacer:

- (void)testSample 
{ 
    BOOL __block didRunBlock = NO; 
    void (^yourBlock)(void) = ^(void) { 
     NSLog(@"on main queue!"); 
     didRunBlock = YES; 
    }; 

    if ([NSThread isMainThread]) 
     yourBlock(); 
    else 
     dispatch_sync(dispatch_get_main_queue(), yourBlock); 

    STAssertEquals(YES, didRunBlock, @"FAIL!"); 
} 
+1

Estar en el hilo principal no garantiza que esté ejecutándose en la cola principal. Son cosas diferentes. ** La cola principal se ejecuta en el hilo principal **, pero podría haber otras colas ejecutándose en ella. –

1

¿Alguna vez salir de la casa si tiene que esperar por sí mismo para salir de casa por primera vez? ¡Lo adivinaste! ¡No! :]

Básicamente, si:

  1. Usted está en FooQueue.(no tiene que ser main_queue)
  2. Llamar al método utilizando sync es decir, de forma serial y desea ejecutar en FooQueue.

¡Nunca sucederá por la misma razón por la que nunca saldrás de casa!

¡Nunca será despachado porque tiene que esperar a que salga de la cola!

Cuestiones relacionadas