web-dev-qa-db-fra.com

en utilisant dispatch_sync dans Grand Central Dispatch

Quelqu'un peut-il expliquer avec des cas d'utilisation très clairs quel est le but de dispatch_sync dans GCD est pour? Je ne peux pas comprendre où et pourquoi je devrais utiliser cela.

Merci!

74
Rasputin Jones

Vous l'utilisez lorsque vous souhaitez exécuter un bloc et attendre les résultats.

Un exemple de ceci est le modèle dans lequel vous utilisez une file d'attente de répartition au lieu de verrous pour la synchronisation. Par exemple, supposons que vous avez un NSMutableArray partagé a, avec un accès médiatisé par la file d'attente de répartition q. Un thread d'arrière-plan peut être ajouté au tableau (asynchrone), tandis que votre thread de premier plan extrait le premier élément (de manière synchrone):

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
        }
});
76
David Gelhar

Comprenez d'abord son frère dispatch_async

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

Tu utilises dispatch_async pour créer un nouveau fil. Lorsque vous faites cela, le thread actuel ne s'arrête pas. Cela signifie //Do More Stuff peut être exécuté avant //Do something else terminer

Que se passe-t-il si vous souhaitez que le thread actuel s'arrête?

Vous n'utilisez pas du tout l'expédition. Écrivez simplement le code normalement

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

Maintenant, disons que vous voulez faire quelque chose sur un DIFFÉRENT thread et attendez comme si et assurez-vous que les trucs sont faits consécutivement.

Il y a de nombreuses raisons de le faire. La mise à jour de l'interface utilisateur, par exemple, se fait sur le thread principal.

C'est là que vous utilisez dispatch_sync

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

Ici, vous avez //Do something//Do something else et //Do More stuff fait consécutivement même si //Do something else se fait sur un autre thread.

Habituellement, lorsque les gens utilisent un fil différent, le but est que quelque chose puisse être exécuté sans attendre. Supposons que vous souhaitiez télécharger une grande quantité de données mais que vous souhaitiez conserver une interface utilisateur fluide.

Par conséquent, dispatch_sync est rarement utilisé. Mais c'est là. Personnellement, je n'ai jamais utilisé ça. Pourquoi ne pas demander un exemple de code ou un projet qui utilise dispatch_sync.

77
user4951

dispatch_sync est sémantiquement équivalent à un verrou mutex traditionnel.

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

fonctionne de la même manière que

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

David Gelhar n'a pas dit que son exemple ne fonctionnera que parce qu'il a créé tranquillement une file d'attente série (passé NULL dans dispatch_queue_create ce qui est égal à DISPATCH_QUEUE_SERIAL).

Si vous souhaitez créer une file d'attente simultanée (pour gagner toute la puissance du multithread), son code entraînera un crash en raison de la mutation NSArray (addObject :) pendant la mutation (removeObjectAtIndex :) ou même d'un mauvais accès (plage NSArray au-delà des limites). Dans ce cas, nous devons utiliser la barrière pour garantir un accès exclusif à NSArray pendant l'exécution des deux blocs. Non seulement il exclut toutes les autres écritures sur le NSArray pendant son exécution, mais il exclut également toutes les autres lectures, ce qui rend la modification sûre.

Exemple de file d'attente simultanée devrait ressembler à ceci:

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.
4
Krzysztof Przygoda

Si vous voulez des exemples d'utilisation pratique, regardez cette question:

Comment résoudre ce blocage qui se produit occasionnellement?

Je le résous en m'assurant que mon managedObjectContext principal est créé sur le thread principal. Le processus est très rapide et cela ne me dérange pas d'attendre. Ne pas attendre signifie que je devrai faire face à beaucoup de problèmes de concordance.

J'ai besoin de dispatch_sync car du code doit être fait sur le thread principal, qui est le thread différent de celui où coder est en cours d'exécution.

Donc, fondamentalement, si vous voulez que le code soit 1. Procédez comme d'habitude. Vous ne voulez pas vous soucier des conditions de course. Vous voulez vous assurer que le code est terminé avant de continuer. 2. Fait sur un fil différent

utilisez dispatch_sync.

Si 1 est violé, utilisez dispatch_async. Si 2 est violé, écrivez simplement le code comme d'habitude.

Jusqu'à présent, je ne fais cela qu'une seule fois, c'est-à-dire lorsque quelque chose doit être fait sur le thread principal.

Voici donc le code:

+(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]];
}
3
user4951

dispatch_sync est principalement utilisé dans le bloc dispatch_async pour effectuer certaines opérations sur le thread principal (comme la mise à jour de l'interface utilisateur).

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;
    });
});
2
Hari Kunwar

Voici un exemple à mi-chemin réaliste. Vous avez 2000 fichiers Zip que vous souhaitez analyser en parallèle. Mais la bibliothèque Zip n'est pas thread-safe. Par conséquent, tout travail touchant la bibliothèque Zip est placé dans la file d'attente unzipQueue. (L'exemple est en Ruby, mais tous les appels sont mappés directement à la bibliothèque C. "Appliquer", par exemple, mappe sur 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
0
nes1983