Concurrence et multithreading en Swift : niveau avancé

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é

\n\n

Les applications modernes pour iOS et macOS nécessitent des performances élevées et une interface réactive. Le travail avancé avec la concurrence en Swift va bien au-delà de la simple utilisation de Grand Central Dispatch (GCD). Dans cet article, nous examinerons les outils clés de Swift 5.5+ pour la gestion des tâches asynchrones : async/await, Actors, TaskGroup et le protocole Sendable. Vous apprendrez à éviter les conditions de course, à structurer correctement le code multithread et à optimiser les performances.

\n\n

1. Bases de async/await et Structured Concurrency

\n\n

Les mots-clés async et await sont le fondement de la concurrence moderne en Swift. Ils permettent d'écrire du code asynchrone qui ressemble à du code synchrone, ce qui simplifie grandement la lecture et le débogage.

\n\n

Déclaration d'une fonction asynchrone :

\n
func fetchUserData() async throws -> User {\n    let url = URL(string: "https://api.example.com/user")!\n    let (data, _) = try await URLSession.shared.data(from: url)\n    let decoder = JSONDecoder()\n    return try decoder.decode(User.self, from: data)\n}
\n\n

Appel d'une fonction asynchrone avec gestion des erreurs :

\n
Task {\n    do {\n        let user = try await fetchUserData()\n        print("User name: \\(user.name)")\n    } catch {\n        print("Failed to fetch user: \\(error)")\n    }\n}
\n\n

Points importants :

\n
    \n
  • Structured Concurrency : Chaque tâche asynchrone a une durée de vie claire, limitée à la portée du Task parent.
  • \n
  • Annulation des tâches : Annulation automatique des tâches enfants lors de l'annulation du Task parent.
  • \n
  • Priorités : Utilisez Task(priority: .userInitiated) pour les tâches importantes pour l'utilisateur.
  • \n
\n\n

2. Actors : protection contre les conditions de course

\n\n

Avant Swift 5.5, les développeurs utilisaient souvent des files d'attente GCD sérialisées pour protéger l'état partagé. Les Actors fournissent une isolation intégrée : l'accès à l'état d'un actor n'est possible que via await, ce qui garantit la sécurité des threads au niveau du compilateur.

\n\n

Exemple d'Actor pour un compte bancaire :

\n
actor BankAccount {\n    private var balance: Double = 0.0\n\n    func deposit(amount: Double) {\n        balance += amount\n    }\n\n    func withdraw(amount: Double) throws {\n        guard balance >= amount else {\n            throw BankError.insufficientFunds\n        }\n        balance -= amount\n    }\n\n    func getBalance() -> Double {\n        return balance\n    }\n}\n\n// Utilisation\nlet account = BankAccount()\nTask {\n    await account.deposit(amount: 100.0)\n    let currentBalance = await account.getBalance()\n    print("Balance: \\(currentBalance)")\n}
\n\n

Avantages des Actors :

\n
    \n
  • Vérification par le compilateur : Toute tentative d'accès à une propriété d'un actor sans await provoquera une erreur de compilation.
  • \n
  • Réentrance (Reentrancy) : Un actor peut suspendre l'exécution de la méthode en cours (par exemple, lors d'un await à l'intérieur de l'actor) et traiter un autre appel, ce qui empêche les interblocages.
  • \n
  • Performances : Les Actors utilisent des mécanismes de synchronisation optimisés, souvent plus rapides que les verrous manuels.
  • \n
\n\n

3. TaskGroup : traitement parallèle des données

\n\n

Lorsque vous devez exécuter plusieurs opérations asynchrones indépendantes et attendre leur achèvement, TaskGroup est l'outil idéal. Il gère automatiquement le nombre de tâches parallèles et collecte les résultats.

\n\n

Exemple de téléchargement parallèle de plusieurs images :

\n
func downloadImages(urls: [URL])                

Blogs

Book Recommendations