dispatch_sync
fait deux choses:
- la file d'attente d'un bloc
- bloque le thread courant jusqu'à ce que le bloc n'a pas fini de courir
Étant donné que le thread principal est une série de file d'attente (ce qui signifie qu'il utilise un seul thread), la déclaration suivante:
dispatch_sync(dispatch_get_main_queue(), ^(){/*...*/});
va provoquer les événements suivants:
-
dispatch_sync
files d'attente du bloc dans la file d'attente principale.
-
dispatch_sync
bloque le thread de la file d'attente principale jusqu'à ce que le bloc a terminé l'exécution.
-
dispatch_sync
attend jamais, parce que le thread où le bloc est censé fonctionner est bloqué.
La clé de la compréhension de ce qu' dispatch_sync
de non-exécution des blocs, il ne les met en attente. L'exécution aura lieu sur une future itération de l'exécution de la boucle.
L'approche suivante:
if (queueA == dispatch_get_current_queue()){
block();
} else {
dispatch_sync(queueA,block);
}
est parfaitement bien, mais sachez qu'il ne vous protège pas de scénarios complexes impliquant une hiérarchie des files d'attente. Dans de tels cas, le courant de la file d'attente peut être différent de celui précédemment bloqué file d'attente lorsque vous essayez d'envoyer votre bloc. Exemple:
dispatch_sync(queueA, ^{
dispatch_sync(queueB, ^{
// dispatch_get_current_queue() is B, but A is blocked,
// so a dispatch_sync(A,b) will deadlock.
dispatch_sync(queueA, ^{
// some task
});
});
});
Pour les cas complexes, de lecture/écriture de données clé-valeur dans la file d'attente de dispatch:
dispatch_queue_t workerQ = dispatch_queue_create("com.meh.sometask", NULL);
dispatch_queue_t funnelQ = dispatch_queue_create("com.meh.funnel", NULL);
dispatch_set_target_queue(workerQ,funnelQ);
static int kKey;
// saves string "funnel" in funnelQ
CFStringRef tag = CFSTR("funnel");
dispatch_queue_set_specific(funnelQ,
&kKey,
(void*)tag,
(dispatch_function_t)CFRelease);
dispatch_sync(workerQ, ^{
// is funnelQ in the hierarchy of workerQ?
CFStringRef tag = dispatch_get_specific(&kKey);
if (tag){
dispatch_sync(funnelQ, ^{
// some task
});
} else {
// some task
}
});
Explication:
- J'ai créer un
workerQ
de la file d'attente qui pointe vers un funnelQ
de la file d'attente. Dans le code réel, c'est utile si vous avez plusieurs "travailleur" les files d'attente et que vous souhaitez reprendre, de suspendre tous à la fois (ce qui est réalisé par la reprise/mise à jour de leur cible funnelQ
de la file d'attente).
- J'ai peut-entonnoir mon travailleur files d'attente à n'importe quel point dans le temps, afin de savoir si ils sont canalisés ou pas, je balise
funnelQ
avec le mot "entonnoir".
- En bas de la route j'
dispatch_sync
quelque chose d' workerQ
, et pour quelque raison que ce soit, je veux dispatch_sync
de funnelQ
, mais en évitant un dispatch_sync à la file d'attente en cours, donc je check pour le tag et d'agir en conséquence. Parce que la obtenir des promenades de la hiérarchie, la valeur ne sera pas trouvé, en workerQ
, mais il se trouve dans funnelQ
. C'est une façon de découvrir si une file d'attente dans la hiérarchie est celui où nous avons stocké la valeur. Et donc, pour éviter un dispatch_sync à la file d'attente en cours.
Si vous vous interrogez sur les fonctions de lecture/écriture des données de contexte, il y a trois:
-
dispatch_queue_set_specific
: Écrire dans une file d'attente.
-
dispatch_queue_get_specific
: Lire à partir d'une file d'attente.
-
dispatch_get_specific
: Le confort de la fonction de lecture à partir de la file d'attente en cours.
La clé est comparé par pointeur, et de ne jamais déréférencé. Le dernier paramètre dans le setter est un destructeur de libérer la clé.
Si vous vous demandez à propos de "pointant vers une file d'attente à l'autre", il veut dire exactement. Par exemple, je peux point de, une file d'attente à la file d'attente principale, et il sera la cause de tous les blocs dans la file d'attente à exécuter dans la file d'attente principale (généralement, cela se fait pour les mises à jour de l'INTERFACE utilisateur).