usando dispatch_sync en Grand Central Dispatch

¿Alguien puede explicar con casos de uso realmente claros para qué es el propósito de dispatch_sync en GCD ? No puedo entender dónde y por qué tendría que usar esto.

¡Gracias!

Lo usa cuando quiere ejecutar un bloque y esperar los resultados.

Un ejemplo de esto es el patrón donde está utilizando una cola de despacho en lugar de lockings para la sincronización. Por ejemplo, supongamos que tiene una NSMutableArray a compartida, con acceso mediado por la cola de despacho q . Un hilo de fondo se puede agregar a la matriz (asincrónico), mientras que el hilo de primer plano está sacando el primer elemento (sincrónicamente):

 NSMutableArray *a = [[NSMutableArray alloc] init]; // All access to `a` is via this dispatch queue! dispatch_queue_t q = dispatch_queue_create("com.foo.samplequeue", NULL); dispatch_async(q, ^{ [a addObject:something]; }); // append to array, non-blocking __block Something *first = nil; // "__block" to make results from block available dispatch_sync(q, ^{ // note that these 3 statements... if ([a count] > 0) { // ...are all executed together... first = [a objectAtIndex:0]; // ...as part of a single block... [a removeObjectAtIndex:0]; // ...to ensure consistent results } }); 

Primero comprende su hermano dispatch_async

 //Do something dispatch_async(queue, ^{ //Do something else }); //Do More Stuff 

Utiliza dispatch_async para crear un nuevo hilo. Cuando haces eso, el hilo actual no se detiene. Eso significa que //Do More Stuff se puede ejecutar antes de //Do something else termine

¿Qué sucede si quieres que se detenga el hilo actual?

No usas despacho en absoluto. Simplemente escribe el código normalmente

 //Do something //Do something else //Do More Stuff 

Ahora, supongamos que quiere hacer algo en un subproceso DIFERENTE y, sin embargo, esperar como si y asegurarse de que las cosas se hagan de forma consecutiva .

Hay muchas razones para hacer esto. La actualización de la interfaz de usuario, por ejemplo, se realiza en el hilo principal.

Ahí es donde usas dispatch_sync

 //Do something dispatch_sync(queue, ^{ //Do something else }); //Do More Stuff 

Aquí tienes //Do something //Do something else y //Do More stuff consecutiva, aunque //Do something else en un hilo diferente.

Usualmente, cuando las personas usan diferentes hilos, el propósito es que algo se pueda ejecutar sin esperar. Supongamos que desea descargar una gran cantidad de datos pero desea mantener la interfaz de usuario sin problemas.

Por lo tanto, dispatch_sync se usa raramente. Pero está ahí. Yo personalmente nunca usé eso. ¿Por qué no solicitar un código de muestra o proyecto que sí utiliza dispatch_sync?

dispatch_sync es semánticamente equivalente a un locking mutex tradicional.

 dispatch_sync(queue, ^{ //access shared resource }); 

funciona igual que

 pthread_mutex_lock(&lock); //access shared resource pthread_mutex_unlock(&lock); 

Si quiere algunas muestras de uso práctico, mire esta pregunta mía:

¿Cómo puedo resolver este punto muerto que ocurre de vez en cuando?

Lo resuelvo asegurando que mi main managedObjectContext se crea en el hilo principal. El proceso es muy rápido y no me importa esperar. No esperar significa que tendré que lidiar con un montón de problemas de concurencia.

Necesito dispatch_sync porque es necesario hacer algún código en el hilo principal, que es el hilo diferente del que se está ejecutando el código.

Entonces, básicamente, si quiere que el código sea 1. Proceda como siempre. No debes preocuparte por las condiciones de carrera. Desea asegurarse de que el código esté completo antes de continuar. 2. Hecho en un hilo diferente

use dispatch_sync.

Si se infringe 1, use dispatch_async. Si se infringe 2, simplemente escriba el código como de costumbre.

Hasta ahora, solo hago esto una vez, es decir, cuando algo debe hacerse en el hilo principal.

Así que aquí está el código:

 +(NSManagedObjectContext *)managedObjectContext { NSThread *thread = [NSThread currentThread]; //BadgerNewAppDelegate *delegate = [BNUtilitiesQuick appDelegate]; //NSManagedObjectContext *moc = delegate.managedObjectContext; if ([thread isMainThread]) { //NSManagedObjectContext *moc = [self managedObjectContextMainThread]; return [self managedObjectContextMainThread]; } else{ dispatch_sync(dispatch_get_main_queue(),^{ [self managedObjectContextMainThread];//Access it once to make sure it's there }); } // a key to cache the context for the given thread NSMutableDictionary *managedObjectContexts =[self thread].managedObjectContexts; @synchronized(self) { if ([managedObjectContexts objectForKey:[self threadKey]] == nil ) { NSManagedObjectContext *threadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; threadContext.parentContext = [self managedObjectContextMainThread]; //threadContext.persistentStoreCoordinator= [self persistentStoreCoordinator]; //moc.persistentStoreCoordinator;// [moc persistentStoreCoordinator]; threadContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; [managedObjectContexts setObject:threadContext forKey:[self threadKey]]; } } return [managedObjectContexts objectForKey:[self threadKey]]; } 

David Gelhar no dijo que su ejemplo funcionará solo porque silenciosamente creó cola serial (pasó NULL en dispatch_queue_create lo que es igual a DISPATCH_QUEUE_SERIAL).

Si desea crear una cola simultánea (para obtener toda la potencia multithread), su código provocará un locking debido a la mutación NSArray (addObject 🙂 durante la mutación (removeObjectAtIndex 🙂 o incluso al acceso incorrecto (NSArray range beyond bounds). En ese caso, deberíamos usar la barrera para asegurar el acceso exclusivo a NSArray mientras ambos bloques se ejecutan. No solo excluye todas las demás escrituras en NSArray mientras se ejecuta, sino que también excluye todas las demás lecturas, lo que hace que la modificación sea segura.

El ejemplo de cola concurrente debería verse así:

 NSMutableArray *a = [[NSMutableArray alloc] init]; // All access to `a` is via this concurrent dispatch queue! dispatch_queue_t q = dispatch_queue_create("com.foo.samplequeue", DISPATCH_QUEUE_CONCURRENT); // append to array concurrently but safely and don't wait for block completion dispatch_barrier_async(q, ^{ [a addObject:something]; }); __block Something *first = nil; // pop 'Something first' from array concurrently and safely but wait for block completion... dispatch_barrier_sync(q, ^{ if ([a count] > 0) { first = [a objectAtIndex:0]; [a removeObjectAtIndex:0]; } }); // ... then here you get your 'first = [a objectAtIndex:0];' due to synchronised dispatch. // If you use async instead of sync here, then first will be nil. 

dispatch_sync se usa principalmente dentro del bloque dispatch_async para realizar algunas operaciones en el hilo principal (como update ui).

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //Update UI in main thread dispatch_sync(dispatch_get_main_queue(), ^{ self.view.backgroundColor = color; }); }); 

Aquí hay un ejemplo a mitad de camino realista. Tienes 2000 archivos zip que deseas analizar en paralelo. Pero la biblioteca zip no es segura para subprocesos. Por lo tanto, todo el trabajo que toca la biblioteca zip entra en la cola unzipQueue . (El ejemplo está en Ruby, pero todas las llamadas se asignan directamente a la biblioteca de C. “apply”, por ejemplo, maps a dispatch_apply (3) )

 #!/usr/bin/env macruby -w require 'rubygems' require 'zip/zipfilesystem' @unzipQueue = Dispatch::Queue.new('ch.unibe.niko.unzipQueue') def extractFile(n) @unzipQueue.sync do Zip::ZipFile.open("Quelltext.zip") { |zipfile| sourceCode = zipfile.file.read("graph.php") } end end Dispatch::Queue.concurrent.apply(2000) do |i| puts i if i % 200 == 0 extractFile(i) end 

Utilicé la sincronización de despacho cuando estaba dentro de un envío asíncrono para indicar que la UI cambia de nuevo al hilo principal.

Mi bloque asíncrono se detiene un poco y sé que el hilo principal está al tanto de los cambios en la interfaz de usuario y los activará. Generalmente, esto se usa en un bloque de código de procesamiento que requiere cierto tiempo de CPU, pero aún quiero hacer cambios en la UI desde ese bloque. Actuar sobre los cambios de la interfaz de usuario en el bloque asíncrono es inútil ya que la interfaz de usuario se ejecuta en el hilo principal. Activarlos también como bloques asíncronos secundarios, o un auto delegado, hace que la interfaz de usuario solo los vea unos segundos más tarde y se ve tarde.

Bloque de ejemplo:

 dispatch_queue_t myQueue = dispatch_queue_create("my.dispatch.q", 0); dispatch_async(myQueue, ^{ // Do some nasty CPU intensive processing, load file whatever if (somecondition in the nasty CPU processing stuff) { // Do stuff dispatch_sync(dispatch_get_main_queue(),^{/* Do Stuff that affects UI Here */}); } });