Cuando usamos GCD, queremos esperar hasta que se ejecuten y realicen dos bloques asíncronos antes de pasar a los siguientes pasos de ejecución. ¿Cuál es la mejor manera de hacer eso?
Intentamos lo siguiente, pero no parece funcionar:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block1
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block2
});
// wait until both the block1 and block2 are done before start block3
// how to do that?
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block3
});
Respuestas:
Use grupos de despacho: consulte aquí para ver un ejemplo, "Esperando grupos de tareas en cola" en el capítulo "Colas de despacho" de la Guía de programación de concurrencia de la Biblioteca de desarrolladores iOS de Apple
Su ejemplo podría verse así:
y podría producir resultados como este:
fuente
dispatch_group_async
es igual quedispatch_async
con un parámetro de grupo agregado. Entonces, si usa diferentes colas para el bloque1 y el bloque2 o las programa en la misma cola concurrente, pueden ejecutarse simultáneamente; si los programa en la misma cola en serie, se ejecutarán en serie. No es diferente de programar los bloques sin grupos.Ampliando la respuesta de Jörn Eyrich (vota su respuesta si votaste esta), si no tienes control sobre las
dispatch_async
llamadas de tus bloques, como podría ser el caso de los bloques de finalización asíncrona, puedes usar los grupos GCD usandodispatch_group_enter
ydispatch_group_leave
directamente.En este ejemplo, pretendemos que
computeInBackground
es algo que no podemos cambiar (imagine que es una devolución de llamada delegada, NSURLConnection completeHandler, o lo que sea), y por lo tanto no tenemos acceso a las llamadas de despacho.En este ejemplo, computeInBackground: complete: se implementa como:
Salida (con marcas de tiempo de una ejecución):
fuente
dispatch_queue_notify
probablemente sea mejor (a menos que se garantice que el tiempo de bloqueo sea corto).Con Swift 5.1, Grand Central Dispatch ofrece muchas formas de resolver su problema. Según sus necesidades, puede elegir uno de los siete patrones que se muestran en los siguientes fragmentos de Playground.
# 1 El uso de
DispatchGroup
,DispatchGroup
'snotify(qos:flags:queue:execute:)
, yDispatchQueue
' sasync(group:qos:flags:execute:)
La Guía de programación de concurrencia para desarrolladores de Apple establece lo siguiente
DispatchGroup
:# 2 El uso de
DispatchGroup
,DispatchGroup
'swait()
,DispatchGroup
' senter()
, yDispatchGroup
'sleave()
Tenga en cuenta que también puede mezclar
DispatchGroup
wait()
conDispatchQueue
async(group:qos:flags:execute:)
o mezclarDispatchGroup
enter()
yDispatchGroup
leave()
conDispatchGroup
notify(qos:flags:queue:execute:)
.# 3 El uso y la 's
DispatchWorkItemFlags
barrier
DispatchQueue
async(group:qos:flags:execute:)
El tutorial Grand Central Dispatch para Swift 4: Parte 1/2 del artículo de Raywenderlich.com da una definición de barreras :
Uso:
# 4. El uso de
DispatchWorkItem
,DispatchWorkItemFlags
'sbarrier
, yDispatchQueue
' sasync(execute:)
# 5. El uso de
DispatchSemaphore
,DispatchSemaphore
'swait()
, yDispatchSemaphore
' ssignal()
Soroush Khanlou escribió las siguientes líneas en la publicación del blog The GCD Handbook :
Apple Developer API Reference también ofrece la siguiente discusión para el
DispatchSemaphore
init(value:)
inicializador:Uso:
# 6. El uso
OperationQueue
y laOperation
'saddDependency(_:)
La Referencia de la API para desarrolladores de Apple establece sobre
OperationQueue
:Uso:
# 7. El uso de
OperationQueue
yOperationQueue
'saddBarrierBlock(_:)
(requiere iOS 13)fuente
Otra alternativa de GCD es una barrera:
Simplemente cree una cola simultánea, envíe sus dos bloques y luego envíe el bloque final con barrera, lo que hará que espere a que los otros dos terminen.
fuente
sleep()
! Solo agregué esassleep()
llamadas por razones pedagógicas, para que los bloques se ejecuten el tiempo suficiente para que pueda ver que se ejecutan simultáneamente. En este ejemplo trivial, en ausencia desleep()
, estos dos bloques pueden ejecutarse tan rápido que el bloque despachado puede comenzar y finalizar antes de que tenga la oportunidad de observar empíricamente la ejecución concurrente. Pero no lo hagassleep()
en tu propio código.Sé que preguntaste por GCD, pero si querías,
NSOperationQueue
también maneja este tipo de cosas con mucha gracia, por ejemplo:fuente
NSOperation
subclase que es concurrente y se estableceisFinished
cuando se completa el proceso asincrónico. Entonces las dependencias funcionan bien.dispatch_semaphore_wait
no tenga lugar en la cola principal y mientras tus señales y esperas estén equilibradas). Siempre que no bloquee la cola principal, un enfoque de semáforo está bien, si no necesita la flexibilidad de las operaciones (por ejemplo, tener la capacidad de cancelarlas, la capacidad de controlar el grado de concurrencia, etc.).maxConcurrentOperationCount
a1
. También puede establecer la prioridad de las operaciones, tanto laqualityOfService
yqueuePriority
, pero estas tienen un impacto mucho más sutil en la prioridad de la tarea que las dependencias y / o el grado de concurrencia de la cola.Las respuestas anteriores son geniales, pero todas se perdieron una cosa. group ejecuta tareas (bloques) en el hilo donde ingresó cuando usa
dispatch_group_enter
/dispatch_group_leave
.esto se ejecuta en la cola concurrente creada
demoQueue
. Si no creo ninguna cola, se ejecuta en el hilo principal .y hay una tercera forma de hacer que las tareas se ejecuten en otro hilo:
Por supuesto, como se mencionó, puede usar
dispatch_group_async
para obtener lo que desea.fuente
La primera respuesta es esencialmente correcta, pero si desea la forma más simple de lograr el resultado deseado, aquí hay un ejemplo de código independiente que muestra cómo hacerlo con un semáforo (que también es cómo funcionan los grupos de despacho detrás de escena, JFYI) :
fuente
dispatch_semaphore_wait
. Tienes dos señales, por lo que necesitas dos esperas. Tal como está, su bloque de "finalización" comenzará tan pronto como el primer bloque indique el semáforo, pero antes de que finalice el otro bloque; 2. Dado que esta era una pregunta de iOS, desalentaría el uso dedispatch_main
.dispatch_semaphore_wait
desbloqueará tan pronto comodispatch_semaphore_signal
se llame a cualquiera de los métodos. La razón por la que puede parecer que esto funciona es que losprintf
bloques 'uno' y 'dos' ocurren inmediatamente, yprintf
el 'finalmente' ocurre después de una espera, por lo tanto, después de que el bloque uno ha dormido durante 2 segundos. Si coloca el printf después de lassleep
llamadas, obtendrá el resultado para 'uno', luego 2 segundos después para 'finalmente', luego 2 segundos más tarde para 'dos'.Respuesta aceptada rápidamente:
fuente
Ejemplo de Swift 4.2:
fuente
group.leave()
causó accidentePor no decir que otras respuestas no son excelentes para ciertas circunstancias, pero este es un fragmento que siempre uso en Google:
fuente