dispatch_sync dentro de dispatch_sync provoca interlocking

Acabo de leer esto en objc.io yendo completamente asíncrono, pero no puedo encontrar una buena explicación

dispatch_queue_t queueA; // assume we have this dispatch_sync(queueA, ^(){ // (a) dispatch_sync(queueA, ^(){ // (b) foo(); }); }); 

Una vez que hayamos alcanzado el segundo dispatch_sync nos bloquearemos: no podemos enviarnos a queueA, porque alguien (el hilo actual) ya está en esa queue y nunca lo va a dejar.

Siempre y cuando entienda

  1. dispatch_sync simplemente agregue el elemento de trabajo (evito usar la palabra "bloque", ya que puede confundir) a la queue, entonces este elemento de trabajo se enviará a la queue de destino de la queue, entonces GCD preservará un hilo threadWorkItem para este elemento de trabajo
  2. Cuando alcanzo (b), estoy en el subprocess threadWorkItem (supongamos que threadWorkItem es el nombre de este subprocess), así que creo que enqueuer otro elemento de trabajo para queueA no es un problema. Pero algunas personas dicen que en este momento, queueA se conserva, queueA está bloqueada -> provoca interlocking, lo que me confunde

Ya leí muchos hilos relacionados con esto, como Deadlock con dispatch_sync , ¿Por qué no podemos usar un dispatch_sync en la queue actual? , ¿Por qué esta llamada dispatch_sync () está congelada? , … pero no puede encontrar una buena explicación. Algunos dicen que dispatch_sync bloquea la queue, algunos dicen que bloquea el hilo actual, … 🙁

Entonces, ¿por qué causa el interlocking?

El dispatch_sync bloquea el hilo actual hasta que el código enviado finaliza, y si está enviando sincrónicamente desde una queue en serie, por lo tanto, también está bloqueando la queue de manera efectiva. Por lo tanto, si envía de forma sincronizada desde la queue de serie a sí mismo, se produce un interlocking.

Pero para ser claro, dispatch_sync bloquea el hilo actual, no la queue actual. Cuando se trata de una queue simultánea, se utilizará un hilo de trabajo diferente para el bloque enviado posteriormente y no se obtienen resultados de interlocking.

Parece que está respondiendo a una discusión al final del capítulo Colas de envío de la Guía de progtwigción de concurrency, que dice:

No llame a la function dispatch_sync desde una tarea que se está ejecutando en la misma queue que pasa a su llamada de function. Si lo hace, se bloqueará la queue. Si necesita enviar a la queue actual, hágalo asíncronamente usando la function dispatch_async .

Eso no es del todo correcto. Si (a) está haciendo esto con una queue simultánea; y (b) hay hilos de trabajo disponibles, esto no causará un punto muerto. Pero es una mala práctica y debe evitarse, no obstante.

Utilice este código para realizar llamadas desde cualquier subprocess al subprocess principal sin riesgo de interlocking. Tenga en count que si hay una jerarquía profunda de queues, aún puede quedar bloqueado.

 static inline void dispatch_synchronized (dispatch_queue_t queue, dispatch_block_t block) { dispatch_queue_set_specific (queue, (__bridge const void *)(queue), (void *)1, NULL); if (dispatch_get_specific ((__bridge const void *)(queue))) block (); else dispatch_sync (queue, block); }