In Swift wird zur Gewährleistung der Thread-Sicherheit häufig GCD (Grand Central Dispatch) und Warteschlangen (DispatchQueue), NSLock sowie moderne Mechanismen wie actor (seit Swift 5.5) verwendet.
Grundidee – Der Zugriff auf veränderbare Daten erfolgt entweder strikt über serielle Warteschlangen oder unter Verwendung von Synchronisatoren (Locks).
Beispiel mit GCD:
class ThreadSafeArray<Element> { private var array: [Element] = [] private let queue = DispatchQueue(label: "com.example.arrayQueue", attributes: .concurrent) func append(_ item: Element) { queue.async(flags: .barrier) { self.array.append(item) } } func get(index: Int) -> Element? { var result: Element? queue.sync { result = self.array.indices.contains(index) ? self.array[index] : nil } return result } }
Moderner Ansatz: Actors (Swift 5.5+):
actor SafeCounter { private var value = 0 func increment() { value += 1 } func get() -> Int { return value } }
Beide Ansätze vermeiden Datenrennen und gewährleisten die Konsistenz des Zustands.
Frage:
Was passiert, wenn Sie
DispatchQueue.syncinnerhalb der Hauptwarteschlange verwenden und warum?
Antwort: Es tritt ein Deadlock auf, weil die Hauptwarteschlange bereits eine Aufgabe ausführt und auf den Abschluss einer synchronen Operation in derselben Warteschlange wartet. Daher kann die Warteschlange die nächste Aufgabe nicht bearbeiten, bis die aktuelle abgeschlossen ist, was niemals geschehen wird.
Beispiel:
DispatchQueue.main.sync { // Deadlock: Diese Zeile wird niemals ausgeführt }
Geschichte
Im Projekt wurde eine veränderbare Sammlung verwendet, die gleichzeitig von mehreren Threads ohne Synchronisierung angesprochen wurde. Dies führte zu Abstürzen der Anwendung und schwer fassbaren Bugs aufgrund von Datenrennen: Gelegentlich gingen Elemente verloren, manchmal gab es einen Array-Überlauf.
Geschichte
Ein Entwickler verwendete DispatchQueue.sync fälschlicherweise in der Hauptwarteschlange zur Laufzeit, was zu einem Deadlock und einem vollständigen Stopp der Benutzeroberfläche nach einem Update führte. Ein dringendes Fix und ein Rollback der Version waren erforderlich.
Geschichte
Bei dem Versuch, eine Klasse mit NSLock thread-sicher zu machen, wurde vergessen, Lock/Unlock für alle Ausstiegswege aus der Methode zu implementieren (zum Beispiel im Fehlerfall oder bei einem Return innerhalb eines Guards), was zu einem Potenzial für Deadlocks und ernsthafte Leistungsprobleme der Anwendung führte.