Vous posez les bonnes questions mais je pense que vous êtes un peu confus (surtout à cause des messages pas très clairs sur ce sujet sur internet).
Concurrent / Série
Voyons comment créer une nouvelle file d'attente de distribution :
let serialQueue = DispatchQueue(label: label)
Si vous ne spécifiez aucun autre paramètre supplémentaire, cette file d'attente se comportera comme une file d'attente en série : Cela signifie que chaque bloc distribué sur cette file (sync ou async, peu importe) sera exécuté seul, sans possibilité pour d'autres blocs d'être exécutés, sur cette même file, simultanément.
Cela ne signifie pas que tout le reste est arrêté, cela signifie simplement que si quelque chose d'autre est distribué sur cette même file d'attente, il attendra que le premier bloc soit terminé avant de commencer son exécution. Les autres threads et files d'attente continueront à fonctionner de manière autonome.
Vous pouvez cependant créer une file d'attente concurrente, qui ne contraindra pas ces blocs de code de cette manière et, au lieu de cela, s'il arrive que plusieurs blocs de code soient distribués sur cette même file d'attente en même temps, elle les exécutera en même temps (sur différents threads).
let concurrentQueue = DispatchQueue(label: label,
qos: .background,
attributes: .concurrent,
autoreleaseFrequency: .inherit,
target: .global())
Ainsi, il suffit de passer l'attribut concurrent
dans la file d'attente, et il ne sera plus en série.
(Je ne parlerai pas des autres paramètres car ils ne font pas l'objet de cette question particulière et, je pense, vous pouvez les lire dans l'autre billet de SO dont le lien figure dans le commentaire ou, si cela ne suffit pas, vous pouvez poser une autre question)
Si vous voulez en savoir plus sur les files d'attente concurrentes (aka : passez si vous ne vous souciez pas des files d'attente concurrentes)
On pourrait se demander : quand ai-je besoin d'une file d'attente concurrente ?
Par exemple, imaginons un cas d'utilisation où vous voulez synchroniser des lectures sur une ressource partagée : puisque les lectures peuvent être effectuées simultanément sans problème, vous pouvez utiliser une file d'attente concurrente pour cela.
Mais que faire si vous voulez écrire sur cette ressource partagée ? Eh bien, dans ce cas, une écriture doit agir comme une "barrière" et pendant l'exécution de cette écriture, aucune autre écriture et aucune lecture ne peuvent opérer sur cette ressource simultanément. Pour obtenir ce type de comportement, le code swift ressemblerait à ceci
concurrentQueue.async(flags: .barrier, execute: { /*your barriered block*/ })
En d'autres termes, vous pouvez faire en sorte qu'une file d'attente concurrente fonctionne temporairement comme une file d'attente en série en cas de besoin.
Une fois encore, la distinction simultanée/série n'est valable que pour les blocs distribués à cette même file d'attente, elle n'a rien à voir avec d'autres travaux simultanés ou en série qui peuvent être effectués sur un autre thread/une autre file d'attente.
SYNC / ASYNC
Il s'agit d'un tout autre problème, qui n'a pratiquement aucun lien avec le précédent.
Ces deux façons de distribuer un bloc de code sont relatives au fil d'exécution/à la file d'attente où vous vous trouvez au moment de l'appel de distribution. Cet appel de distribution bloque (en cas de synchronisation) ou ne bloque pas (asynchronisation) l'exécution de ce thread/queue pendant l'exécution du code que vous distribuez sur l'autre queue.
Disons que j'exécute une méthode et que dans cette méthode, je dispatche quelque chose d'asynchrone sur une autre file d'attente (j'utilise la file d'attente principale mais cela peut être n'importe quelle file d'attente) :
func someMethod() {
var aString = "1"
DispatchQueue.main.async {
aString = "2"
}
print(aString)
}
Ce qui se passe, c'est que ce bloc de code est distribué sur une autre file d'attente et pourrait être exécuté en série ou simultanément sur cette file, mais cela n'a aucune corrélation avec ce qui se passe sur la file d'attente actuelle (qui est celle sur laquelle someMethod est appelé).
Ce qui se passe dans la file d'attente actuelle est que le code va continuer à s'exécuter et n'attendra pas que ce bloc soit terminé pour imprimer cette variable. Cela signifie que, très probablement, vous verrez l'impression de 1 et non de 2 (plus précisément, vous ne pouvez pas savoir ce qui se passera en premier).
Si, au contraire, vous l'aviez envoyé en synchro, vous auriez TOUJOURS imprimé 2 au lieu de 1, parce que la file d'attente actuelle aurait attendu que ce bloc de code soit terminé avant de poursuivre son exécution.
Donc, cela va imprimer 2 :
func someMethod() {
var aString = "1"
DispatchQueue.main.sync {
aString = "2"
}
print(aString)
}
Mais cela signifie-t-il que la file d'attente sur laquelle une certaine méthode est appelée est réellement arrêtée ?
Eh bien, cela dépend de la file d'attente actuelle :
- Si c'est une série, alors oui. Tous les blocs précédemment distribués dans cette file d'attente ou qui seront distribués dans cette file devront attendre que ce bloc soit terminé.
- Si c'est un concurrent, alors non. Tous les blocs concurrents continueront leur exécution, seul ce bloc d'exécution spécifique sera bloqué, attendant que cet appel de distribution termine son travail. Bien sûr, si nous sommes dans le cas avec barrière, alors c'est comme pour les files d'attente en série.
Que se passe-t-il lorsque la file d'attente actuelle et la file d'attente sur laquelle nous répartissons sont les mêmes ?
En supposant que nous sommes sur des files d'attente en série (ce qui, je pense, sera la plupart de vos cas d'utilisation).
- Dans le cas où nous expédions la synchronisation, que l'impasse. Rien ne sera plus jamais exécuté sur cette queue. C'est le pire qui puisse arriver.
- Dans le cas d'une distribution asynchrone, le code sera exécuté à la fin de tout le code déjà distribué sur cette file d'attente (y compris, mais sans s'y limiter, le code qui s'exécute actuellement dans someMethod).
Soyez donc très prudent lorsque vous utilisez la méthode de synchronisation et assurez-vous que vous n'êtes pas sur la même file d'attente que celle dans laquelle vous êtes envoyé.
J'espère que cela vous a permis de mieux comprendre.