Parallelität und Multithreading in Swift: Fortgeschrittenes Niveau

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Parallelität und Multithreading in Swift: Fortgeschrittenes Niveau

\n\n

Moderne Anwendungen für iOS und macOS erfordern hohe Leistung und Reaktionsfähigkeit der Benutzeroberfläche. Die fortgeschrittene Arbeit mit Parallelität in Swift geht weit über die einfache Nutzung von Grand Central Dispatch (GCD) hinaus. In diesem Artikel betrachten wir die wichtigsten Werkzeuge von Swift 5.5+ zur Verwaltung asynchroner Aufgaben: async/await, Actors, TaskGroup und das Protokoll Sendable. Sie erfahren, wie Sie Datenrennen vermeiden, Multithreading-Code richtig strukturieren und die Leistung optimieren.

\n\n

1. Grundlagen von async/await und Structured Concurrency

\n\n

Die Schlüsselwörter async und await sind die Grundlage moderner Parallelität in Swift. Sie ermöglichen es, asynchronen Code zu schreiben, der wie synchroner Code aussieht, was das Lesen und Debuggen erheblich vereinfacht.

\n\n

Deklaration einer asynchronen Funktion:

\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

Aufruf einer asynchronen Funktion mit Fehlerbehandlung:

\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

Wichtige Punkte:

\n
    \n
  • Structured Concurrency: Jede asynchrone Aufgabe hat eine klare Lebensdauer, die durch den Gültigkeitsbereich des übergeordneten Task begrenzt ist.
  • \n
  • Abbrechen von Aufgaben: Automatisches Abbrechen von untergeordneten Aufgaben beim Abbrechen des übergeordneten Tasks.
  • \n
  • Prioritäten: Verwenden Sie Task(priority: .userInitiated) für Aufgaben, die für den Benutzer wichtig sind.
  • \n
\n\n

2. Actors: Schutz vor Datenrennen

\n\n

Vor Swift 5.5 verwendeten Entwickler oft serialisierte GCD-Warteschlangen, um gemeinsamen Zustand zu schützen. Actors bieten eine eingebaute Isolation: Der Zugriff auf den Zustand eines Actors ist nur über await möglich, was die Threadsicherheit auf Compiler-Ebene garantiert.

\n\n

Beispiel eines Actors für ein Bankkonto:

\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// Verwendung\nlet account = BankAccount()\nTask {\n    await account.deposit(amount: 100.0)\n    let currentBalance = await account.getBalance()\n    print("Balance: \\(currentBalance)")\n}
\n\n

Vorteile von Actors:

\n
    \n
  • Compiler-Prüfung: Der Versuch, auf eine Eigenschaft eines Actors ohne await zuzugreifen, führt zu einem Compiler-Fehler.
  • \n
  • Wiedereintritt (Reentrancy): Ein Actor kann die Ausführung der aktuellen Methode anhalten (z. B. bei await innerhalb des Actors) und einen anderen Aufruf bearbeiten, was Deadlocks verhindert.
  • \n
  • Leistung: Actors verwenden optimierte Synchronisationsmechanismen, die oft schneller sind als manuelle Sperren.
  • \n
\n\n

3. TaskGroup: Parallele Datenverarbeitung

\n\n

Wenn mehrere unabhängige asynchrone Operationen ausgeführt und auf deren Abschluss gewartet werden müssen, ist TaskGroup das ideale Werkzeug. Es verwaltet automatisch die Anzahl der parallelen Aufgaben und sammelt die Ergebnisse.

\n\n

Beispiel für das parallele Herunterladen mehrerer Bilder:

\n
func downloadImages(urls: [URL])                

Blogs

Buchempfehlungen