Sobes.tech
Middle+

Ako synchronizovať údaje medzi vláknami a zabezpečiť konzistentnosť kontextu?

sobes.tech AI

Odpoveď od AI

Na synchronizáciu údajov a zabezpečenie konzistencie kontextu v viacvláknovej aplikácii na iOS sa používajú nasledujúce prístupy:

  1. NSLock / Lock: Jednoduché mutexy na blokovanie kritických sekcií kódu.

    import Foundation
    
    class ThreadSafeCounter {
        private var count: Int = 0
        private let lock = NSLock()
    
        func increment() {
            lock.lock()
            defer { lock.unlock() } // Zaručené odblokovanie
            count += 1
        }
    
        func getCount() -> Int {
            lock.lock()
            defer { lock.unlock() }
            return count
        }
    }
    
  2. NSRecursiveLock: Umožňuje opätovne získať zámok z toho istého vlákna.

    import Foundation
    
    class RecursiveLocker {
        private let lock = NSRecursiveLock()
    
        func firstMethod() {
            lock.lock()
            defer { lock.unlock() }
            print("Prvá metóda")
            secondMethod() // Opätovné získanie je možné
        }
    
        func secondMethod() {
            lock.lock()
            defer { lock.unlock() }
            print("Druhá metóda")
        }
    }
    
  3. NSCondition / NSConditionLock: Umožňuje vláknam čakať na určitú podmienku (Condition) alebo hodnotu (ConditionLock) pred pokračovaním vo vykonávaní. Používa sa na implementáciu producer-consumer vzoru.

    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() // Čaká, kým sa neuvoľní miesto
            }
    
            items.append(item)
            print("Pridané: \(item), Aktuálny buffer: \(items)")
            condition.signal() // Signalizuje čakajúcim vláknam
        }
    
        func removeItem() -> Int {
            condition.lock()
            defer { condition.unlock() }
    
            while items.isEmpty {
                condition.wait() // Čaká, kým sa neobjaví prvok
            }
    
            let item = items.removeFirst()
            print("Odstránené: \(item), Aktuálny buffer: \(items)")
            condition.signal() // Signalizuje čakajúcim vláknam
            return item
        }
    }
    
  4. **Grand Central Dispatch (GCD):

    • Serial Queues: Vykonávajú úlohy postupne, po jednej naraz na danom vlákne, čo zabezpečuje atómovosť prístupu ku zdrojom.

    • Concurrent Queues s Barrier Flags: Umožňujú vykonávať čítanie súbežne, ale zápisové operácie (write) sa vykonávajú exkluzívne. Toto implementuje model read-write lock.

      import Foundation
      
      class ThreadSafeArray<Element> {
          private var array: [Element] = []
          private let queue =
          DispatchQueue(label: "com.mycompany.threadsafearray", attributes: .concurrent) // Súbežná fronta
      
          func append(_ newElement: Element) {
              queue.async(flags: .barrier) { // Barrier pre zápis
                  self.array.append(newElement)
              }
          }
      
          func get(at index: Int) -> Element? {
              var result: Element?
              queue.sync { // Synchronizácia pre čítanie (alebo async, ak poradie čítania nie je dôležité)
                  if index < self.array.count {
                      result = self.array[index]
                  }
              }
              return result
          }
      }
      
  5. Operácie Core Data Context: Každý NSManagedObjectContext má vlastný súkromný serializovaný vlákno. Prístup k objektom a kontextu by sa mal vykonávať iba z tohto vlákna.

    import CoreData
    
    // Príklad uloženia do Core Data kontextu
    let context = // Získaný kontext
    context.perform { // Vykonanie na súkromnom vlákne kontextu
        // Práca s objektmi Core Data
        do {
            try context.save()
        } catch {
            // Spracovanie chyby
        }
    }
    

Voľba metódy závisí od konkrétnej úlohy a požiadaviek na výkon. GCD s radovými frontami alebo súbežnými frontami s bariérami je často najpreferovanejšou voľbou kvôli svojej efektívnosti a jednoduchosti použitia. Pre Core Data nevyhnutne používajte metódy perform alebo performAndWait.