Concurrence et multithreading en Swift : techniques avancées

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Concurrence et multithreading en Swift : niveau avancé

La concurrence (concurrency) est l'un des sujets les plus complexes et importants dans le développement iOS/macOS. Au niveau avancé, Swift propose des outils puissants : du classique Grand Central Dispatch (GCD) au modèle moderne async/await et au système strict des Actors. Dans cet article, nous analyserons comment gérer efficacement les threads, éviter les conditions de course et écrire du code thread-safe.



1. Grand Central Dispatch (GCD) : travailler avec des files d'attente et des groupes

GCD reste la base du multithreading en Swift. Cependant, au niveau avancé, il est important de comprendre non seulement la différence entre main et global(), mais aussi de savoir gérer les dépendances entre tâches.

Création de files d'attente personnalisées

let serialQueue = DispatchQueue(label: "com.pythonlib.serial")let concurrentQueue = DispatchQueue(label: "com.pythonlib.concurrent", attributes: .concurrent)

serialQueue.async { print("Tâche 1 dans une file d'attente séquentielle")}concurrentQueue.async { print("Tâche 2 dans une file d'attente parallèle")}

DispatchGroup : synchronisation de plusieurs tâches

let group = DispatchGroup()let queue = DispatchQueue.global()

queue.async(group: group) { sleep(1) print("Chargement des données 1")}queue.async(group: group) { sleep(2) print("Chargement des données 2")}

group.notify(queue: DispatchQueue.main) { print("Tous les chargements terminés, mise à jour de l'UI")}

Utilisez DispatchGroup lorsque vous devez attendre la fin de plusieurs opérations parallèles (par exemple, chargement d'images ou analyse JSON).



2. Modèle moderne async/await et Task

Depuis Swift 5.5, le langage a obtenu un support natif de l'asynchronisme via les mots-clés async et await. Cela simplifie la lecture du code et élimine la "pyramide des closures".

Déclaration et appel d'une fonction asynchrone

func fetchUserData() async -> String {    // Simulation d'une requête réseau    try? await Task.sleep(nanoseconds: 2_000_000_000)    return "Données utilisateur chargées"}

Task { let result = await fetchUserData() print(result) // Affiche "Données utilisateur chargées"}

Appels parallèles avec async let

func loadImage() async -> Data { return Data() }func loadMetadata() async -> String { return "meta" }

Task { async let image = loadImage() async let metadata = loadMetadata() let result = try await (image, metadata) print("Image : \\(result.0), métadonnées : \\(result.1)")}

async let permet de lancer plusieurs tâches asynchrones en parallèle et d'attendre qu'elles soient toutes terminées. C'est plus efficace qu'un appel séquentiel.



3. Actors : sécurité des threads au niveau du langage

Les acteurs (Actors) sont un nouveau mécanisme de protection contre les conditions de course. Ils garantissent que l'accès à leur état est automatiquement synchronisé.

Définition et utilisation d'un acteur

actor BankAccount {    private var balance: Double = 0.0        func deposit(amount: Double) {        balance += amount        print("Solde après dépôt : \\(balance)")    }        func withdraw(amount: Double) -> Bool {        guard balance >= amount else { return false }        balance -= amount        return true    }        func getBalance() -> Double { balance }}

Task { let account = BankAccount() await account.deposit(amount: 100) let success = await account.withdraw(amount: 50) print("Retrait réussi : \\(success)")}

Toutes les méthodes de l'acteur sont isolées par défaut — on ne peut les appeler qu'avec await. Cela exclut toute modification accidentelle des données depuis plusieurs threads.

Méthodes non isolées (nonisolated)

actor Logger {    nonisolated func log(message: String) {        print("Log : \\(message)")\                

Blogs

Book Recommendations