Sobes.tech
Middle+

Wie synchronisiert man Daten zwischen Threads und stellt die Konsistenz des Kontexts sicher?

sobes.tech KI

Antwort von AI

Zur Synchronisierung von Daten und zur Gewährleistung der Konsistenz des Kontexts in einer Multithread-Anwendung auf iOS werden die folgenden Ansätze verwendet:

  1. NSLock / Lock: Einfache Mutex zum Sperren kritischer Codeabschnitte.

    import Foundation
    
    class ThreadSafeCounter {
        private var count: Int = 0
        private let lock = NSLock()
    
        func increment() {
            lock.lock()
            defer { lock.unlock() } // Garantierte Freigabe des Locks
            count += 1
        }
    
        func getCount() -> Int {
            lock.lock()
            defer { lock.unlock() }
            return count
        }
    }
    
  2. NSRecursiveLock: Erlaubt, das Lock erneut vom selben Thread zu erwerben.

    import Foundation
    
    class RecursiveLocker {
        private let lock = NSRecursiveLock()
    
        func firstMethod() {
            lock.lock()
            defer { lock.unlock() }
            print("Erste Methode")
            secondMethod() // Wiederholtes Erwerben ist möglich
        }
    
        func secondMethod() {
            lock.lock()
            defer { lock.unlock() }
            print("Zweite Methode")
        }
    }
    
  3. NSCondition / NSConditionLock: Ermöglichen es Threads, auf eine bestimmte Bedingung oder einen Wert zu warten, bevor sie fortfahren. Wird für das Producer-Consumer-Muster verwendet.

    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() // Warten, bis Platz frei wird
            }
    
            items.append(item)
            print("Hinzugefügt: \(item), Aktueller Buffer: \(items)")
            condition.signal() // Signalisieren an wartende Threads
        }
    
        func removeItem() -> Int {
            condition.lock()
            defer { condition.unlock() }
    
            while items.isEmpty {
                condition.wait() // Warten, bis ein Element vorhanden ist
            }
    
            let item = items.removeFirst()
            print("Entfernt: \(item), Aktueller Buffer: \(items)")
            condition.signal() // Signalisieren an wartende Threads
            return item
        }
    }
    
  4. Grand Central Dispatch (GCD):

    • Serial Queues: Führen Aufgaben sequenziell aus, jeweils eine auf einmal auf dem gleichen Thread, was den atomaren Zugriff auf Ressourcen gewährleistet.

    • Concurrent Queues mit Barrier Flags: Ermöglichen paralleles Lesen, aber Schreiboperationen (write) werden exklusiv ausgeführt. Dies implementiert ein Read-Write-Lock-Modell.

      import Foundation
      
      class ThreadSafeArray<Element> {
          private var array: [Element] = []
          private let queue =
          DispatchQueue(label: "com.mycompany.threadsafearray", attributes: .concurrent) // Concurrency-Queue
      
          func append(_ newElement: Element) {
              queue.async(flags: .barrier) { // Barrier für Schreibzugriffe
                  self.array.append(newElement)
              }
          }
      
          func get(at index: Int) -> Element? {
              var result: Element?
              queue.sync { // Synchronisation für Lesezugriffe (oder async, wenn Reihenfolge nicht wichtig ist)
                  if index < self.array.count {
                      result = self.array[index]
                  }
              }
              return result
          }
      }
      
  5. Core Data Context-Operationen: Jeder NSManagedObjectContext hat seinen eigenen privaten serialisierten Thread. Der Zugriff auf Objekte und den Kontext sollte nur von diesem Thread erfolgen.

    import CoreData
    
    // Beispiel für das Speichern im Core Data-Kontext
    let context = // Erhaltener Kontext
    context.perform { // Ausführung im privaten Thread des Kontexts
        // Arbeit mit Core Data-Objekten hier
        do {
            try context.save()
        } catch {
            // Fehlerbehandlung
        }
    }
    

Die Wahl der Methode hängt von der spezifischen Aufgabe und den Leistungsanforderungen ab. GCD mit serialisierten oder concurrenten Queues mit Barrieren ist oft die bevorzugte Methode wegen ihrer Effizienz und Benutzerfreundlichkeit. Für Core Data verwenden Sie immer die Methoden perform oder performAndWait.