7

¿Hay alguna forma de simular una instrucción break en un bloque dispatch_apply()?Cómo "romper" de dispatch_apply()?

por ejemplo, todas las API de cacao que he visto tratar con bloques de enumeración tiene un parámetro de "stop":

[array enumerateObjectsUsingBlock:^(id obj, NSUInteger i, BOOL *stop) { 
    if ([obj isNotVeryNice]) { 
     *stop = YES; // No more enumerating! 
    } else { 
     NSLog(@"%@ at %zu", obj, i); 
    } 
}]; 

¿Hay algo similar para GCD?

Respuesta

4

No creo que dispatch_apply lo admita. La mejor manera en que se me ocurre imitarlo sería crear una variable booleana __block y verificarla al comienzo del bloque. Si está configurado, rescatar rápidamente. Todavía tendría que ejecutar el bloque en el resto de las iteraciones, pero sería más rápido.

1

No se puede break a dispatch_apply ya que es ilógico.

En -enumerateObjectsUsingBlock: una pausa está bien definida porque las funciones se ejecutan secuencialmente. Pero en dispatch_apply las funciones se ejecutan en paralelo. Esto significa que en la invocación de i=3 del "bloque", la llamada i=4 podría haberse iniciado. Si tiene break en i=3, ¿aún debería ejecutarse la llamada i=4?

@BJ es lo más cercano que puede hacer, pero siempre habrá algún "derrame".

+0

Bueno, eso no es del todo cierto; Si bien es cierto que enumerateObjectsUsingBlock: es secuencial, también hay enumerateObjectsWithOptions: usingBlock :. Ese parámetro de "opciones" se puede usar para indicar que la enumeración debe realizarse al mismo tiempo. No estoy seguro de cómo lo están haciendo internamente, pero supongo que se hace usando un dispatch_group que permitiría un control más directo. –

+0

Pero el punto es que enumerateObjectsWithOptions: usingBlock: todavía tiene el parámetro * stop. –

+1

Sería lógico admitir detenerse en dispatch_apply(), pero no tenía sentido dentro de los objetivos de diseño. Declarar que la bandera de detención en 'enumerateObjectsUsingBlock:' existe debido a la ejecución secuencial implícita es incorrecta; los dos son completamente ortogonales. – bbum

14

Por diseño, dispatch_*() API no tienen noción de cancelación. La razón de esto es porque es casi universalmente cierto que su código mantiene el concepto de cuándo detener o no y, por lo tanto, también admite que en el despacho _ *() las API serían redundantes (y, con redundancia, vienen los errores).

Por lo tanto, si desea "detenerse antes" o cancelar los elementos pendientes en una cola de envío (independientemente de cómo fueron en cola), puede hacerlo compartiendo un poco de estado con los bloques en cola que le permiten cancelar.

if (is_canceled()) return; 

O:

__block BOOL keepGoing = YES; 
dispatch_*(someQueue, ^{ 
    if (!keepGoing) return; 
    if (weAreDoneNow) keepGoing = NO; 
} 

Tenga en cuenta que tanto enumerateObjectsUsingBlock: y enumerateObjectsWithOptions:usingBlock: tanto cancelación de apoyo debido a que la API está en un papel diferente. La llamada al método de enumeración es sincrónica, incluso si la ejecución real de los bloques enumeradores puede ser totalmente simultánea dependiendo de las opciones.

Por lo tanto, establecer *stopFlag=YES le indica a la enumeración que se detenga. Sin embargo, no garantiza que se detendrá inmediatamente en el caso concurrente. La enumeración puede, de hecho, ejecutar unos cuantos bloques ya en cola antes de detenerse.

(Se podría pensar brevemente que sería más razonable devolver BOOL para indicar si la enumeración debería continuar. Hacerlo habría requerido que el bloque de enumeración se ejecutara sincrónicamente, incluso en el caso concurrente, de modo que el valor de retorno podría comprobarse. Esto hubiera sido mucho menos eficiente.)