Introduction à la concurrence et au multithreading en Swift
Les applications mobiles et de bureau modernes doivent être rapides et réactives. Dans le développement iOS et macOS, la gestion correcte de la concurrence et du multithreading joue un rôle clé. Swift fournit des outils puissants pour l'exécution parallèle de tâches, allant du classique Grand Central Dispatch (GCD) au modèle moderne async/await et aux acteurs.
Dans cet article, nous passerons en revue toutes les approches principales pour travailler avec les threads en Swift : des concepts de base aux techniques avancées. Vous apprendrez à éviter les race conditions, les deadlocks et autres problèmes typiques de la programmation multithread.
Grand Central Dispatch (GCD) — le classique du multithreading
Grand Central Dispatch (GCD) est une bibliothèque bas niveau d'Apple pour gérer l'exécution parallèle du code. Elle utilise des files d'attente (dispatch queues) pour distribuer les tâches entre les threads.
Types principaux de files d'attente
Dans GCD, il existe deux types principaux de files d'attente :
- Serial queues (files séquentielles) : les tâches s'exécutent les unes après les autres dans l'ordre d'ajout. Idéales pour synchroniser l'accès aux ressources.
- Concurrent queues (files parallèles) : les tâches peuvent s'exécuter simultanément, l'ordre d'achèvement n'est pas garanti.
Exemple de travail avec GCD
import Foundation
// Création d'une file séquentiellelet serialQueue = DispatchQueue(label: "com.example.serial")
// Création d'une file parallèlelet concurrentQueue = DispatchQueue(label: "com.example.concurrent", attributes: .concurrent)
// Exécution asynchroneserialQueue.async { print("Tâche 1 exécutée en arrière-plan")}
// Exécution synchrone (bloque le thread actuel)serialQueue.sync { print("Tâche 2 exécutée de manière synchrone")}
// Travail avec le thread principalDispatchQueue.main.async { print("Mise à jour de l'UI sur le thread principal")}
// Exécution différéeDispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { print("Exécuté après 2 secondes")}Dispatch Group — synchronisation d'un groupe de tâches
Dispatch Group permet de suivre l'achèvement d'un groupe de tâches asynchrones et d'exécuter du code après leur fin.
let group = DispatchGroup()let queue = DispatchQueue.global(qos: .background)
for i in 1...5 { queue.async(group: group) { print("Tâche \\(i) commencée") Thread.sleep(forTimeInterval: Double.random(in: 0.5...2.0)) print("Tâche \\(i) terminée") }}
group.notify(queue: DispatchQueue.main) { print("Toutes les tâches sont terminées ! Mise à jour de l'UI.")}async/await moderne en Swift 5.5+
Depuis Swift 5.5, Apple a introduit un nouveau modèle de concurrence basé sur les mots-clés async et await. Cela rend le code asynchrone plus lisible et compréhensible.
Définition de fonctions asynchrones
Les fonctions asynchrones sont marquées par le mot-clé async, et leur appel nécessite await :
import Foundation
// Fonction asynchrone de chargement de donnéesfunc fetchUserData() async throws -> String { let url = URL(string: "https://api.example.com/user")! let (data, _) = try await URLSession.shared.data(from: url) return String(data: data, encoding: .utf8) ?? "Aucune donnée"}
// Utilisation de async/awaitfunc loadUserProfile() async { do { let userData = try await fetchUserData() await MainActor.run { print("Données utilisateur : \\(userData)") } } catch { print("Erreur de chargement : \\(error)") }}
// Lancement d'une tâche asynchroneTask { await loadUserProfile()}Async let — exécution parallèle
Le mot-clé async let permet de lancer plusieurs tâches asynchrones en parallèle :
<