Einführung in Concurrency und Multithreading in Swift
Moderne iOS- und macOS-Apps erfordern zunehmend die gleichzeitige Ausführung mehrerer Aufgaben. Ob das Laden von Daten aus dem Netzwerk, die Bildverarbeitung oder komplexe mathematische Berechnungen – ohne eine durchdachte Thread-Verwaltung geht es nicht. In Swift sind Concurrency und Multithreading auf höchstem Niveau implementiert und bieten dem Entwickler leistungsstarke und sichere Werkzeuge.
In diesem Artikel betrachten wir drei grundlegende Ansätze: Grand Central Dispatch (GCD), OperationQueue und das moderne Schlüsselwort async/await, das in Swift 5.5 eingeführt wurde. Sie erfahren, wie Sie richtig zwischen ihnen wählen und typische Fehler wie Race Conditions und Deadlocks vermeiden.
Grand Central Dispatch (GCD) – der Klassiker des Multithreading in Swift
Grand Central Dispatch (GCD) ist eine Low-Level-Bibliothek von Apple, die einen Thread-Pool verwaltet. Der Entwickler muss Threads nicht manuell erstellen und zerstören – GCD erledigt dies automatisch, indem es Aufgaben auf die verfügbaren Prozessorkerne verteilt.
Grundlegende GCD-Warteschlangen
In GCD gibt es zwei Arten von Warteschlangen: serielle (serial) und parallele (concurrent). Aufgaben in einer seriellen Warteschlange werden nacheinander ausgeführt, während sie in einer parallelen Warteschlange gleichzeitig gestartet werden können.
// Erstellen einer seriellen Warteschlangelet serialQueue = DispatchQueue(label: "com.example.serial")serialQueue.async { print("Aufgabe 1")}serialQueue.async { print("Aufgabe 2")}
// Erstellen einer parallelen Warteschlangelet concurrentQueue = DispatchQueue(label: "com.example.concurrent", attributes: .concurrent)concurrentQueue.async { print("Parallele Aufgabe A")}concurrentQueue.async { print("Parallele Aufgabe B")}Globale Warteschlangen und QoS
GCD bietet vorgefertigte globale Warteschlangen mit unterschiedlichen Prioritäten (Quality of Service). Verwenden Sie diese, wenn Sie keine eigene Warteschlange erstellen müssen:
.userInteractive– für UI-Updates (höchste Priorität).userInitiated– für vom Benutzer initiierte Aufgaben.utility– für lang andauernde Hintergrundoperationen.background– für zeitunkritische Aufgaben
DispatchQueue.global(qos: .userInitiated).async { // Schwere Arbeit let result = performComplexCalculation() DispatchQueue.main.async { // Zurück zum Hauptthread für UI-Updates self.updateUI(with: result) }}Wichtig: Aktualisieren Sie die UI immer nur im Hauptthread (DispatchQueue.main). Das Ignorieren dieser Regel führt zu unvorhersehbaren App-Abstürzen.
OperationQueue – Verwaltung von Aufgabenabhängigkeiten
Wenn GCD das „Schweizer Taschenmesser" ist, dann ist OperationQueue ein vollwertiger Planer mit der Möglichkeit zum Abbrechen, zur Priorisierung und zur Erstellung von Abhängigkeiten zwischen Operationen. Jede Operation ist eine Instanz der Klasse Operation (oder ihrer Unterklasse BlockOperation).
Erstellen und Ausführen von Operationen
let operationQueue = OperationQueue()operationQueue.maxConcurrentOperationCount = 2 // Begrenzung der Anzahl paralleler Operationen
let operation1 = BlockOperation { print("Operation 1")}let operation2 = BlockOperation { print("Operation 2")}
// Abhängigkeit hinzufügen: operation2 startet erst nach Abschluss von operation1operation2.addDependency(operation1)
operationQueue.addOperations([operation1, operation2], waitUntilFinished: false)Abbrechen und Prioritäten
Im Gegensatz zu GCD ermöglicht OperationQueue das Abbrechen einer bestimmten Operation oder der gesamten Warteschlange:
// Abbrechen einer einzelnen Operationoperation1.cancel()
// Abbrechen der gesamten WarteschlangeoperationQueue.cancelAllOperations()
// Festlegen der Prioritätoperation1.