W Swift, aby zapewnić bezpieczeństwo wątków, często używa się GCD (Grand Central Dispatch) oraz kolejek (DispatchQueue), NSLock, a także nowoczesnych mechanizmów, takich jak actor (od Swift 5.5).
Podstawowa idea – dostęp do zmiennych danych odbywa się wyłącznie przez kolejki sekwencyjne lub przy użyciu synchronizatorów (blokad).
Przykład z 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 } }
Nowoczesne podejście: Aktorzy (Swift 5.5+):
actor SafeCounter { private var value = 0 func increment() { value += 1 } func get() -> Int { return value } }
Oba podejścia pozwalają uniknąć wyścigów danych i utrzymać spójność stanu.
Pytanie:
Co się stanie, jeśli użyjesz
DispatchQueue.syncwewnątrz Głównej Kolejki i dlaczego?
Odpowiedź: Wystąpi zakleszczenie (Deadlock), ponieważ Główna Kolejka już wykonuje zadanie i czeka na zakończenie operacji synchronizacyjnej w tej samej kolejce. W związku z tym, kolejka nie będzie mogła przetworzyć następnego zadania, dopóki bieżące nie zostanie zakończone, a to nigdy się nie zdarzy.
Przykład:
DispatchQueue.main.sync { // Zakleszczenie: ten wiersz nigdy się nie wykona }
Historia
W projekcie używana była zmienna kolekcja, do której jednocześnie odnosiły się różne wątki bez synchronizacji. To prowadziło do awarii aplikacji i trudnych do uchwycenia błędów z powodu wyścigu danych: okresowo gubione były elementy, czasami występowały przekroczenia granic tablicy.
Historia
Programista niewłaściwie użył DispatchQueue.sync na głównej kolejce w czasie wykonywania, co spowodowało zakleszczenie i całkowite zatrzymanie UI użytkowników po wydaniu aktualizacji. Wymagano pilnego fixu i wycofania wydania.
Historia
Przy próbie uczynienia klasy bezpieczną dla wątków za pomocą NSLock, zapomniano wdrożyć blokadę/odblokadę dla wszystkich ścieżek wyjścia z metody (na przykład w przypadku błędu lub return wewnątrz guard), co prowadziło do potencjalnych zakleszczeń i poważnych problemów z wydajnością aplikacji.