Jak synchronizować dane między wątkami i zapewnić spójność kontekstu?
sobes.tech AI
Odpowiedź od AI
Aby zsynchronizować dane i zapewnić spójność kontekstu w wielowątkowej aplikacji na iOS, stosuje się następujące podejścia:
-
NSLock / Lock: Proste mutexy do blokowania krytycznych sekcji kodu.
import Foundation class ThreadSafeCounter { private var count: Int = 0 private let lock = NSLock() func increment() { lock.lock() defer { lock.unlock() } // Gwarantowane zwolnienie blokady count += 1 } func getCount() -> Int { lock.lock() defer { lock.unlock() } return count } } -
NSRecursiveLock: Pozwala na ponowne nabycie blokady z tego samego wątku.
import Foundation class RecursiveLocker { private let lock = NSRecursiveLock() func firstMethod() { lock.lock() defer { lock.unlock() } print("Pierwsza metoda") secondMethod() // Ponowne nabycie jest możliwe } func secondMethod() { lock.lock() defer { lock.unlock() } print("Druga metoda") } } -
NSCondition / NSConditionLock: Pozwalają wątkom oczekiwać na określony warunek lub wartość przed kontynuacją. Używa się ich do implementacji wzorca producent-konsument.
import Foundation class Buffer { private var items: [Int] = [] private let condition = NSCondition() private let capacity = 5 func addItem(_ item: Int) { condition.lock() defer { condition.unlock() } while items.count == capacity { condition.wait() // Czeka, aż pojawi się wolne miejsce } items.append(item) print("Dodano: \(item), Aktualny bufor: \(items)") condition.signal() // Sygnalizuje oczekującym wątkom } func removeItem() -> Int { condition.lock() defer { condition.unlock() } while items.isEmpty { condition.wait() // Czeka, aż pojawi się element } let item = items.removeFirst() print("Usunięto: \(item), Aktualny bufor: \(items)") condition.signal() // Sygnalizuje oczekującym wątkom return item } } -
Grand Central Dispatch (GCD):
-
Kolejki seryjne: Wykonują zadania sekwencyjnie, jedno po drugim na tym samym wątku, zapewniając atomowość dostępu do zasobów.
-
Kolejki współbieżne z flagami Barrier: Pozwalają na wykonywanie odczytów równocześnie, ale operacje zapisu (write) są wykonywane wyłącznie. Implementuje to model locka odczyt-zapis.
import Foundation class ThreadSafeArray<Element> { private var array: [Element] = [] private let queue = DispatchQueue(label: "com.mycompany.threadsafearray", attributes: .concurrent) // Kolejka współbieżna func append(_ newElement: Element) { queue.async(flags: .barrier) { // Barrier dla zapisu self.array.append(newElement) } } func get(at index: Int) -> Element? { var result: Element? queue.sync { // Synchronizacja dla odczytu (lub async, jeśli kolejność nie ma znaczenia) if index < self.array.count { result = self.array[index] } } return result } }
-
-
Operacje kontekstu Core Data: Każdy
NSManagedObjectContextma własny prywatny, serializowany wątek. Dostęp do obiektów i kontekstu powinien być wykonywany tylko z tego wątku.import CoreData // Przykład zapisywania w kontekście Core Data let context = // Uzyskany kontekst context.perform { // Wykonanie na prywatnym wątku kontekstu // Praca z obiektami Core Data do { try context.save() } catch { // Obsługa błędów } }
Wybór metody zależy od konkretnego zadania i wymagań dotyczących wydajności. GCD z kolejkami seryjnymi lub współbieżnymi z barierami jest często najbardziej preferowaną metodą ze względu na jej efektywność i łatwość użycia. Dla Core Data, zawsze używaj metod perform lub performAndWait.