Voici quelques expériences que j'ai faites pour me comprendre sur ces files d'attente série
, concurrent
avec Grand Central Dispatch
.
func doLongAsyncTaskInSerialQueue() {
let serialQueue = DispatchQueue(label: "com.queue.Serial")
for i in 1...5 {
serialQueue.async {
if Thread.isMainThread{
print("tâche en cours sur le thread principal")
}else{
print("tâche en cours sur le thread arrière-plan")
}
let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
let _ = try! Data(contentsOf: imgURL)
print("\(i) téléchargement complet")
}
}
}
La tâche sera exécutée dans un thread différent (autre que le thread principal) lorsque vous utilisez async dans GCD. Async signifie exécuter la ligne suivante sans attendre que le bloc s'exécute, ce qui entraîne un thread principal et une file d'attente principale non bloquants. Depuis qu'il s'agit d'une file d'attente sérielle, toutes les tâches sont exécutées dans l'ordre où elles sont ajoutées à la file d'attente sérielle. Les tâches exécutées de manière sérielle sont toujours exécutées une par une par le thread unique associé à la file d'attente.
func doLongSyncTaskInSerialQueue() {
let serialQueue = DispatchQueue(label: "com.queue.Serial")
for i in 1...5 {
serialQueue.sync {
if Thread.isMainThread{
print("tâche en cours sur le thread principal")
}else{
print("tâche en cours sur le thread arrière-plan")
}
let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
let _ = try! Data(contentsOf: imgURL)
print("\(i) téléchargement complet")
}
}
}
La tâche peut s'exécuter dans le thread principal lorsque vous utilisez sync dans GCD. Sync exécute un bloc sur une file d'attente donnée et attend sa complétion, ce qui entraîne le blocage du thread principal ou de la file d'attente principale. Comme la file d'attente principale doit attendre que le bloc traité soit terminé, le thread principal sera disponible pour traiter des blocs provenant de files d'attente autres que la file d'attente principale. Par conséquent, il y a une chance que le code exécuté sur la file d'attente arrière-plan puisse réellement s'exécuter sur le thread principal. Étant donné qu'il s'agit d'une file d'attente sérielle, toutes les tâches sont exécutées dans l'ordre où elles sont ajoutées (FIFO).
func doLongASyncTaskInConcurrentQueue() {
let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent)
for i in 1...5 {
concurrentQueue.async {
if Thread.isMainThread{
print("tâche en cours sur le thread principal")
}else{
print("tâche en cours sur le thread arrière-plan")
}
let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
let _ = try! Data(contentsOf: imgURL)
print("\(i) téléchargement complet")
}
print("\(i) en cours d'exécution")
}
}
La tâche s'exécutera dans le thread arrière-plan lorsque vous utilisez async dans GCD. Async signifie exécuter la ligne suivante sans attendre que le bloc s'exécute, ce qui entraîne un thread principal non bloquant. Rappelez-vous dans une file d'attente concurrente, les tâches sont traitées dans l'ordre où elles sont ajoutées à la file d'attente mais avec des threads différents attachés à la file. Rappelez-vous qu'elles ne sont pas censées terminer la tâche dans l'ordre où elles sont ajoutées à la file. L'ordre des tâches diffère à chaque fois que des threads sont créés automatiquement si nécessaire. Les tâches sont exécutées en parallèle. Avec plus que cela (maxConcurrentOperationCount) est atteint, certaines tâches se comporteront comme une série jusqu'à ce qu'un thread soit libre.
func doLongSyncTaskInConcurrentQueue() {
let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent)
for i in 1...5 {
concurrentQueue.sync {
if Thread.isMainThread{
print("tâche en cours sur le thread principal")
}else{
print("tâche en cours sur le thread arrière-plan")
}
let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
let _ = try! Data(contentsOf: imgURL)
print("\(i) téléchargement complet")
}
print("\(i) exécuté")
}
}
La tâche peut s'exécuter dans le thread principal lorsque vous utilisez sync dans GCD. Sync exécute un bloc sur une file d'attente donnée et attend sa complétion, ce qui entraîne le blocage du thread principal ou de la file d'attente principale. Comme la file d'attente principale doit attendre que le bloc traité soit terminé, le thread principal sera disponible pour traiter des blocs provenant de files d'attente autres que la file d'attente principale. Par conséquent, il y a une chance que le code exécuté sur la file d'attente arrière-plan puisse réellement s'exécuter sur le thread principal. Étant donné qu'il s'agit d'une file d'attente concurrente, les tâches peuvent ne pas être terminées dans l'ordre où elles sont ajoutées à la file. Mais avec une opération synchrone, c'est le cas, même si elles peuvent être traitées par différents threads. Ainsi, cela se comporte comme s'il s'agissait de la file d'attente sérielle.
Voici un résumé de ces expériences
Rappelez-vous qu'en utilisant GCD, vous ajoutez uniquement des tâches à la file et exécutez des tâches à partir de cette file. La file envoie vos tâches soit dans le thread principal soit dans un thread arrière-plan en fonction que l'opération soit synchrone ou asynchrone. Les types de files sont File sérielle, Concurrente, File d'attente principale. Toutes les tâches que vous effectuez se font par défaut à partir de la file d'attente principale. Il existe déjà quatre files d'attente concurrentes globales prédéfinies pour votre application à utiliser et une file d'attente principale (DispatchQueue.main). Vous pouvez également créer manuellement votre propre file et exécuter des tâches à partir de cette file.
Les tâches liées à l'interface utilisateur doivent toujours être effectuées à partir du thread principal en planifiant la tâche dans la file principale. L'abréviation est DispatchQueue.main.sync/async
, alors que les opérations liées au réseau ou lourdes doivent toujours être effectuées de manière asynchrone, quelle que soit le thread que vous utilisez, que ce soit le thread principal ou un thread arrière-plan.
MODIFICATION : Cependant, il y a des cas où il est nécessaire d'exécuter des appels réseau de manière synchrone dans un thread arrière-plan sans figer l'interface utilisateur (par exemple, rafraîchir le jeton OAuth et attendre qu'il réussisse ou non). Vous devez envelopper cette méthode dans une opération asynchrone. De cette manière, vos opérations lourdes sont exécutées dans l'ordre et sans bloquer le thread principal.
func doMultipleSyncTaskWithinAsynchronousOperation() {
let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent)
concurrentQueue.async {
let concurrentQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
for i in 1...5 {
concurrentQueue.sync {
let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
let _ = try! Data(contentsOf: imgURL)
print("\(i) téléchargement complet")
}
print("\(i) exécutée")
}
}
}
MODIFICATION MODIFICATION : Vous pouvez regarder la vidéo de démonstration ici