Je joue avec GCD et j'ai écrit une application CoinFlipper jouet.
Voici la méthode qui retourne les pièces:
- (void)flipCoins:(NSUInteger)nFlips{
// Create the queues for work
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL);
// Split the number of flips into whole chunks of kChunkSize and the remainder.
NSUInteger numberOfWholeChunks = nFlips / kChunkSize;
NSUInteger numberOfRemainingFlips = nFlips - numberOfWholeChunks * kChunkSize;
if (numberOfWholeChunks > 0) {
for (NSUInteger index = 0; index < numberOfWholeChunks; index++) {
dispatch_async(queue, ^{
NSUInteger h = 0;
NSUInteger t = 0;
flipTheCoins(kChunkSize, &h, &t);
dispatch_async(mainQueue, ^{
self.nHeads += h;
self.nTails += t;
});
});
}
}
if (numberOfRemainingFlips > 0) {
dispatch_async(queue, ^{
NSUInteger h = 0;
NSUInteger t = 0;
flipTheCoins(numberOfRemainingFlips, &h, &t);
dispatch_async(mainQueue, ^{
self.nHeads += h;
self.nTails += t;
});
});
}
}
Comme vous pouvez le voir; Je divise le nombre de flips en gros morceaux en les retournant en arrière-plan et en mettant à jour les propriétés dans la file d'attente principale. Les propriétés sont observées par le contrôleur de fenêtre et une interface utilisateur est mise à jour avec les résultats en cours d'exécution.
J'ai parcouru le Guide de programmation des accès concurrents et les documents GCD, et bien qu'il existe un moyen de suspendre une file d'attente, il n'y a aucun moyen de les arrêter et de supprimer tous les objets en file d'attente et non en cours d'exécution.
J'aimerais pouvoir brancher un bouton "stop" pour annuler le retournement une fois qu'il a commencé. Avec NSOperationQueue
je peux observer la propriété operationCount
pour savoir si elle est en cours d'exécution et cancelAllOperations
pour supprimer les blocs en file d'attente.
J'ai parcouru le Guide de programmation des accès concurrents et les documents GCD, et bien qu'il existe un moyen de suspendre une file d'attente, il n'y a aucun moyen de les arrêter et de supprimer tous les objets en file d'attente et non en cours d'exécution.
Alors :-
Il s'agit d'une question semi-courante lors de la programmation avec GCD.
La réponse courte est que GCD n'a pas d'API d'annulation pour les files d'attente. La justification:
Dans tous ces cas, il est beaucoup plus efficace et puissant d'écrire du code comme ceci:
dispatch_async(my_obj->queue, ^{
bool done = false;
// do_full_update() takes too long, therefore:
while ( !my_obj->cancelled && !done ) {
done = do_partial_update(my_obj);
}
});
Oh, et pour savoir si une file d'attente a terminé d'exécuter tous les blocs mis en file d'attente, votre code peut simplement exécuter un bloc vide avec l'API synchrone:
dispatch_sync(my_obj->queue, ^{});
Comme mentionné dans les commentaires, une meilleure façon de savoir quand votre travail est terminé est d'utiliser des groupes de répartition. Envoyez tous vos blocs au groupe, puis vous pouvez ajouter un gestionnaire d'achèvement au groupe. Une fois le travail terminé, le bloc d'achèvement sera exécuté.
dispatch_group_t myGroup = dispatch_group_create();
dispatch_group_async(myGroup, my_obj->queue, ^{
bool done = false;
while ( !my_obj->cancelled && !done ) {
done = do_partial_update(my_obj);
}
});
dispatch_group_notify(myGroup, my_obj->queue, ^{
NSLog(@"Work is done!");
dispatch_release(myGroup);
});
Une fois tous vos blocs terminés, le groupe sera vide et déclenchera le bloc de notification. De là, vous pouvez mettre à jour l'interface utilisateur, etc.
Bonne chance et amusez-vous bien!
Comment savoir si fonctionne
BOOL dispatch_queue_is_empty(dispatch_queue_t queue)
{
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(queue, ^{
dispatch_group_leave(group);
});
int64_t maxWaitTime = 0.00000005 * NSEC_PER_SEC;
BOOL isReady = dispatch_group_wait(group, maxWaitTime) == 0;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
});
return isReady;
}
Pour tester dans l'application
dispatch_queue_t queue = dispatch_queue_create("test", 0);
NSLog(@"Is empty %@", dispatch_queue_is_empty(queue) ? @"YES" : @"NO");
dispatch_async(queue, ^{
for(int i = 0; i < 100; i++)
{
NSLog(@"... %i", i);
}
});
NSLog(@"Is empty %@", dispatch_queue_is_empty(queue) ? @"YES" : @"NO");
Résultat
Is empty YES
Is empty NO
... 0
... 1
... 2
... 3
... 4
... 5
... 6
... 7
... 8
... 9
La valeur par défaut de la variable maxWaitTime
peut être modifiée pour obtenir le résultat souhaité.
Si vous avez une file d'attente de répartition série OR une file d'attente de répartition simultanée, voici un code qui peut faire la même chose.
BOOL __block queueIsEmpty = false;
dispatch_barrier_async (_dispatchQueue, ^{
queueIsEmpty = true;
});
while (!queueIsEmpty) {
int i = 0; // NOOP instruction
}
// At this point your queue should be empty.