74 votes

Utilisation de dispatch_sync dans Grand Central Dispatch

Quelqu'un peut-il expliquer, avec des cas d'utilisation très clairs, à quoi sert l'objectif de dispatch_sync en GCD ? Je ne peux pas comprendre où et pourquoi je devrais utiliser cela.

Merci!

77voto

David Gelhar Points 20703

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 de synchronisation. Par exemple, supposons que vous avez un NSMutableArray partagé a , avec un accès médiatisé par la file d'attente de distribution q . Un fil d’arrière-plan peut être ajouté au tableau (asynchrone), alors que votre fil d’avant-plan extrait le premier élément (de façon 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
        }
});
 

77voto

Jim Thio Points 7137

D'abord comprendre son frère, dispatch_async

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

Vous utilisez dispatch_async pour créer un nouveau thread. Lorsque vous faites cela, le thread actuel ne s'arrêtera pas. Cela signifie que //Do More Stuff peut être exécutée avant l' //Do something else finition

Ce qui se passe si vous souhaitez que le thread en cours d'arrêt?

Vous n'utilisez pas l'expédition. Il suffit d'écrire le code normalement

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

Maintenant, disons que vous voulez faire quelque chose sur une DIFFÉRENTS thread et encore attendre comme si et de s'assurer que les choses ont été faites de façon consécutive.

Il y a beaucoup de raisons de le faire. INTERFACE utilisateur mise à jour, par exemple, est 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 obtenu //Faire quelque chose //Faire autre chose et //Faire Plus de choses effectué consécutivement même si //Faire quelque chose d'autre est faite sur un autre thread.

Habituellement, lorsque les gens utilisent thread différent, le but est donc que quelque chose peut exécutée sans attendre. Dites que vous voulez télécharger de gros volumes de données, mais vous voulez garder l'INTERFACE utilisateur lisse.

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

25voto

Catfish_Man Points 15439

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);
 

4voto

David Gelhar gauche non-dit que son exemple ne fonctionne que parce que tranquillement, il a créé une file série (passé NULL dans dispatch_queue_create ce qui est égal à DISPATCH_QUEUE_SERIAL).

Si vous souhaitez créer simultanées de la file d'attente (pour avoir tous multithread de puissance), son code conduire à des crash à cause de NSArray mutation (addObject:) au cours de mutation (removeObjectAtIndex:) ou même la mauvaise accès (NSArray vont au-delà de limites). Dans ce cas, on devrait utiliser de la barrière afin d'assurer un accès exclusif à la NSArray alors que les deux blocs de l'exécuter. Non seulement il exclut toutes les autres écritures pour le NSArray tandis qu'il court, mais il exclut également tous les autres lectures, la modification coffre-fort.

Exemple simultanées dans la file d'attente 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.

3voto

Jim Thio Points 7137

Si vous voulez quelques exemples de l'utilisation pratique de regarder cette question de la mienne:

Comment puis-je résoudre ce blocage qui se produisent occasionnellement?

- Je le résoudre en s'assurant que mon principal managedObjectContext est créé sur le thread principal. Le processus est très rapide et je ne me dérange pas d'attente. Pas d'attente signifie que je vais avoir à traiter avec beaucoup de concurency question.

J'ai besoin de dispatch_sync parce que certains de code doivent être faites sur le thread principal, qui est le thread différent de celui où le code est exécuté.

Donc, fondamentalement, si vous voulez le code de 1. Procéder comme d'habitude. Vous ne voulez pas à vous soucier des conditions de course. Vous voulez vous assurer que le code est terminée avant de passer. 2. Faite sur un autre thread

utilisation dispatch_sync.

Si 1 n'est pas respectée, l'utilisation dispatch_async. Si 2 est violé, il suffit d'écrire le code comme d'habitude.

Jusqu'à présent, j'ai seulement le faire une fois, à savoir quand quelque chose doit être fait sur le thread principal.

Donc voici 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]];
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X