Jak synchronizovat data mezi vlákny a zajistit konzistenci kontextu?
sobes.tech AI
Odpověď od AI
Pro synchronizaci dat a zajištění konzistence kontextu v multi-threaded aplikaci na iOS se používají následující přístupy:
-
NSLock / Lock: Jednoduché mutexy pro blokování kritických sekcí kódu.
import Foundation class ThreadSafeCounter { private var count: Int = 0 private let lock = NSLock() func increment() { lock.lock() defer { lock.unlock() } // Zajištění uvolnění zámku count += 1 } func getCount() -> Int { lock.lock() defer { lock.unlock() } return count } } -
NSRecursiveLock: Umožňuje opakované získání zámku z téže vlákna.
import Foundation class RecursiveLocker { private let lock = NSRecursiveLock() func firstMethod() { lock.lock() defer { lock.unlock() } print("První metoda") secondMethod() // Opakované získání je možné } func secondMethod() { lock.lock() defer { lock.unlock() } print("Druhá metoda") } } -
NSCondition / NSConditionLock: Umožňuje vláknům čekat na určitý stav (Condition) nebo hodnotu (ConditionLock) před pokračováním. Používá se pro implementaci vzoru producer-consumer.
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() // Čeká, dokud nebude volné místo } items.append(item) print("Přidáno: \(item), Aktuální buffer: \(items)") condition.signal() // Signalizuje čekajícím vláknům } func removeItem() -> Int { condition.lock() defer { condition.unlock() } while items.isEmpty { condition.wait() // Čeká, dokud se neobjeví prvek } let item = items.removeFirst() print("Odebráno: \(item), Aktuální buffer: \(items)") condition.signal() // Signalizuje čekajícím vláknům return item } } -
Grand Central Dispatch (GCD):
-
Serial Queues: Vykonávají úkoly sekvenčně, po jednom na daném vlákně, což zajišťuje atomický přístup ke zdrojům.
-
Concurrent Queues s Barrier Flags: Umožňují provádět čtení současně, ale zápisové operace (write) se provádějí výhradně. To implementuje model read-write lock.
import Foundation class ThreadSafeArray<Element> { private var array: [Element] = [] private let queue = DispatchQueue(label: "com.mycompany.threadsafearray", attributes: .concurrent) // Konkurentní fronta func append(_ newElement: Element) { queue.async(flags: .barrier) { // Barrier pro zápis self.array.append(newElement) } } func get(at index: Int) -> Element? { var result: Element? queue.sync { // Synchronizace pro čtení (nebo async, pokud pořadí čtení není důležité) if index < self.array.count { result = self.array[index] } } return result } }
-
-
Operace Core Data Contextu: Každý
NSManagedObjectContextmá svůj vlastní soukromý serializovaný vlákno. Přístup k objektům a kontextu by měl být prováděn pouze z tohoto vlákna.import CoreData // Příklad ukládání do Core Data kontextu let context = // Získaný kontext context.perform { // Vykonání na soukromém vlákně kontextu // Práce s objekty Core Data zde do { try context.save() } catch { // Zpracování chyby } }
Volba metody závisí na konkrétním úkolu a požadavcích na výkon. GCD s frontami serial nebo concurrent s bariérami je často nejpreferovanější přístup díky své efektivitě a jednoduchosti použití. Pro Core Data vždy používejte metody perform nebo performAndWait.