Lors de l'utilisation de GCD, nous voulons attendre que deux blocs asynchrones soient exécutés et terminés avant de passer aux étapes d'exécution suivantes. Quelle est la meilleure façon de le faire?
Nous avons essayé ce qui suit, mais cela ne semble pas fonctionner:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block1
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block2
});
// wait until both the block1 and block2 are done before start block3
// how to do that?
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block3
});
Réponses:
Utiliser des groupes de répartition: voir ici un exemple, "En attente de groupes de tâches en file d'attente" dans le chapitre "Files de répartition" du Guide de programmation de l'accès concurrentiel de la bibliothèque pour développeurs iOS d'Apple
Votre exemple pourrait ressembler à ceci:
et pourrait produire une sortie comme celle-ci:
la source
dispatch_group_async
est juste commedispatch_async
avec un paramètre de groupe ajouté. Ainsi, si vous utilisez des files d'attente différentes pour block1 et block2 ou que vous les planifiez sur la même file d'attente simultanée, elles peuvent s'exécuter simultanément; si vous les programmez sur la même file d'attente série, ils s'exécuteront en série. Ce n'est pas différent de la planification des blocs sans groupes.En développant la réponse de Jörn Eyrich (augmentez sa réponse si vous votez pour celle-ci), si vous n'avez pas de contrôle sur les
dispatch_async
appels pour vos blocs, comme cela pourrait être le cas pour les blocs de complétion asynchrones, vous pouvez utiliser les groupes GCD en utilisantdispatch_group_enter
etdispatch_group_leave
directement.Dans cet exemple, nous prétendons
computeInBackground
être quelque chose que nous ne pouvons pas changer (imaginez que c'est un rappel de délégué, NSURLConnection completionHandler, ou autre), et donc nous n'avons pas accès aux appels de répartition.Dans cet exemple, computeInBackground: completion: est implémenté comme:
Sortie (avec horodatage d'une exécution):
la source
dispatch_queue_notify
soit probablement mieux (à moins que le temps de blocage ne soit garanti court).Avec Swift 5.1, Grand Central Dispatch offre de nombreuses façons de résoudre votre problème. Selon vos besoins, vous pouvez choisir l'un des sept modèles illustrés dans les extraits Playground suivants.
#1. En utilisant
DispatchGroup
,DispatchGroup
'snotify(qos:flags:queue:execute:)
etDispatchQueue
d'async(group:qos:flags:execute:)
Le guide de programmation Apple Developer Concurrency indique à propos de
DispatchGroup
:# 2. Utilisation de
DispatchGroup
,DispatchGroup
'swait()
,DispatchGroup
' senter()
etDispatchGroup
'sleave()
Notez que vous pouvez également mélanger
DispatchGroup
wait()
avecDispatchQueue
async(group:qos:flags:execute:)
ou mélangerDispatchGroup
enter()
etDispatchGroup
leave()
avecDispatchGroup
notify(qos:flags:queue:execute:)
.# 3. L' utilisation et l »
DispatchWorkItemFlags
barrier
DispatchQueue
async(group:qos:flags:execute:)
Grand Central Dispatch Tutorial for Swift 4: L' article de la partie 1/2 de Raywenderlich.com donne une définition des barrières :
Usage:
# 4. En utilisant
DispatchWorkItem
,DispatchWorkItemFlags
'sbarrier
etDispatchQueue
d'async(execute:)
# 5. En utilisant
DispatchSemaphore
,DispatchSemaphore
'swait()
etDispatchSemaphore
d'signal()
Soroush Khanlou a écrit les lignes suivantes dans le billet de blog The GCD Handbook :
La référence de l'API Apple Developer donne également la discussion suivante pour l'
DispatchSemaphore
init(value:)
initialiseur:Usage:
# 6. L' utilisation
OperationQueue
etOperation
l »addDependency(_:)
La référence de l'API Apple Developer indique
OperationQueue
:Usage:
#7. L' utilisation
OperationQueue
etOperationQueue
l »addBarrierBlock(_:)
(nécessite iOS 13)la source
Une autre alternative GCD est un obstacle:
Créez simplement une file d'attente simultanée, distribuez vos deux blocs, puis envoyez le bloc final avec barrière, ce qui le fera attendre que les deux autres se terminent.
la source
sleep()
! Je n'ai ajouté cessleep()
appels que pour des raisons pédagogiques, pour faire fonctionner les blocs suffisamment longtemps pour que vous puissiez voir qu'ils s'exécutent simultanément. Dans cet exemple trivial, en l'absence desleep()
, ces deux blocs peuvent s'exécuter si rapidement que le bloc distribué peut commencer et se terminer avant que vous n'ayez une chance d'observer empiriquement l'exécution simultanée. Mais passleep()
dans votre propre code.Je sais que vous avez posé des questions sur GCD, mais si vous le souhaitez,
NSOperationQueue
gère également ce genre de choses très gracieusement, par exemple:la source
NSOperation
sous-classe qui est simultanée et définieisFinished
lorsque le processus asynchrone se termine. Ensuite, les dépendances fonctionnent correctement.dispatch_semaphore_wait
n'a pas lieu dans la file d'attente principale et tant que vos signaux et vos attentes sont équilibrés). Tant que vous ne bloquez pas la file d'attente principale, une approche sémaphore convient si vous n'avez pas besoin de la flexibilité des opérations (par exemple, avoir la possibilité de les annuler, la capacité de contrôler le degré de concurrence, etc.).maxConcurrentOperationCount
sur1
. Vous pouvez également définir la priorité des opérations, à la fois lesqualityOfService
etqueuePriority
, mais ceux-ci ont un impact beaucoup plus subtil sur la priorité des tâches que les dépendances et / ou le degré de concurrence de la file d'attente.Les réponses ci-dessus sont toutes cool, mais elles ont toutes manqué une chose. group exécute des tâches (blocs) dans le thread où il est entré lorsque vous utilisez
dispatch_group_enter
/dispatch_group_leave
.cela s'exécute dans la file d'attente simultanée créée
demoQueue
. Si je ne crée aucune file d'attente, il s'exécute dans le thread principal .et il existe une troisième façon d'exécuter des tâches dans un autre thread:
Bien sûr, comme mentionné, vous pouvez utiliser
dispatch_group_async
pour obtenir ce que vous voulez.la source
La première réponse est essentiellement correcte, mais si vous voulez le moyen le plus simple d'obtenir le résultat souhaité, voici un exemple de code autonome montrant comment le faire avec un sémaphore (qui est également comment les groupes de répartition fonctionnent en arrière-plan, JFYI) :
la source
dispatch_semaphore_wait
. Vous avez deux signaux, vous avez donc besoin de deux attentes. Tel quel, votre bloc "d'achèvement" commencera dès que le premier bloc signale le sémaphore, mais avant que l'autre bloc ne se termine; 2. Étant donné qu'il s'agissait d'une question iOS, je découragerais l'utilisation dedispatch_main
.dispatch_semaphore_wait
débloquera dès que l'une desdispatch_semaphore_signal
méthodes sera appelée. La raison pour laquelle cela peut sembler fonctionner est que lesprintf
blocs pour «un» et «deux» se produisent immédiatement, et queprintf
pour le «finalement» survient après une attente - donc après que le bloc a dormi pendant 2 secondes. Si vous mettez le printf après lessleep
appels, vous obtiendrez la sortie pour «un», puis 2 secondes plus tard pour «enfin», puis 2 secondes plus tard pour «deux».Réponse acceptée rapidement:
la source
Exemple de Swift 4.2:
la source
group.leave()
a causé un crashPour ne pas dire que les autres réponses ne sont pas idéales dans certaines circonstances, mais voici un extrait que j'utilise toujours depuis Google:
la source