Sobes.tech
Middle+

Hoe synchroniseer je gegevens tussen threads en zorg je voor consistentie van de context?

sobes.tech AI

Antwoord van AI

Voor het synchroniseren van gegevens en het waarborgen van de consistentie van de context in een multi-threaded applicatie op iOS worden de volgende benaderingen gebruikt:

  1. NSLock / Lock: Eenvoudige mutexen voor het blokkeren van kritieke secties van code.

    import Foundation
    
    class ThreadSafeCounter {
        private var count: Int = 0
        private let lock = NSLock()
    
        func increment() {
            lock.lock()
            defer { lock.unlock() } // Garant voor het losmaken van de lock
            count += 1
        }
    
        func getCount() -> Int {
            lock.lock()
            defer { lock.unlock() }
            return count
        }
    }
    
  2. NSRecursiveLock: Maakt herhaalde vergrendeling mogelijk vanuit dezelfde thread.

    import Foundation
    
    class RecursiveLocker {
        private let lock = NSRecursiveLock()
    
        func firstMethod() {
            lock.lock()
            defer { lock.unlock() }
            print("Eerste methode")
            secondMethod() // Herhaalde vergrendeling mogelijk
        }
    
        func secondMethod() {
            lock.lock()
            defer { lock.unlock() }
            print("Tweede methode")
        }
    }
    
  3. NSCondition / NSConditionLock: Laat threads wachten op een bepaalde voorwaarde (Condition) of waarde (ConditionLock) voordat ze doorgaan. Wordt gebruikt voor het implementeren van producer-consumer patronen.

    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() // Wacht tot er ruimte vrij is
            }
    
            items.append(item)
            print("Toegevoegd: \(item), Huidige buffer: \(items)")
            condition.signal() // Signaleer wachtende threads
        }
    
        func removeItem() -> Int {
            condition.lock()
            defer { condition.unlock() }
    
            while items.isEmpty {
                condition.wait() // Wacht tot er een element is
            }
    
            let item = items.removeFirst()
            print("Verwijderd: \(item), Huidige buffer: \(items)")
            condition.signal() // Signaleer wachtende threads
            return item
        }
    }
    
  4. Grand Central Dispatch (GCD):

    • Serial Queues: Voert taken achtereenvolgens uit, één tegelijk op een thread, wat atomair toegang tot resources garandeert.

    • Concurrent Queues met Barrier Flags: Laat lezen gelijktijdig uitvoeren, maar schrijfoperaties (write) worden exclusief uitgevoerd. Dit implementeert een read-write lock model.

      import Foundation
      
      class ThreadSafeArray<Element> {
          private var array: [Element] = []
          private let queue =
          DispatchQueue(label: "com.mycompany.threadsafearray", attributes: .concurrent) // Concurrerende queue
      
          func append(_ newElement: Element) {
              queue.async(flags: .barrier) { // Barrier voor schrijven
                  self.array.append(newElement)
              }
          }
      
          func get(at index: Int) -> Element? {
              var result: Element?
              queue.sync { // Sync voor lezen (of async, als volgorde niet belangrijk is)
                  if index < self.array.count {
                      result = self.array[index]
                  }
              }
              return result
          }
      }
      
  5. Core Data Context operaties: Elke NSManagedObjectContext heeft zijn eigen privé-seriale thread. Toegang tot objecten en de context moet alleen vanaf deze thread plaatsvinden.

    import CoreData
    
    // Voorbeeld van opslaan in een Core Data context
    let context = // Verkregen context
    context.perform { // Uitvoeren op de privé-thread van de context
        // Werken met Core Data objecten hier
        do {
            try context.save()
        } catch {
            // Foutafhandeling
        }
    }
    

De keuze van methode hangt af van de specifieke taak en prestatie-eisen. GCD met seriële queues of gelijktijdige queues met barrières zijn vaak de meest aanbevolen aanpak vanwege hun efficiëntie en gebruiksgemak. Voor Core Data moet je altijd de methoden perform of performAndWait gebruiken.