Sobes.tech
Middle+

Πώς συγχρονίζονται τα δεδομένα μεταξύ νημάτων και πώς διασφαλίζεται η συνοχή του πλαισίου;

sobes.tech AI

Απάντηση από AI

Για τον συγχρονισμό δεδομένων και την διασφάλιση της συνέπειας του πλαισίου σε μια εφαρμογή πολλαπλών νημάτων στο iOS, χρησιμοποιούνται οι ακόλουθες προσεγγίσεις:

  1. NSLock / Lock: Απλά mutex για το κλείδωμα κρίσιμων τμημάτων κώδικα.

    import Foundation
    
    class ThreadSafeCounter {
        private var count: Int = 0
        private let lock = NSLock()
    
        func increment() {
            lock.lock()
            defer { lock.unlock() } // Εγγυάται την απελευθέρωση του κλειδώματος
            count += 1
        }
    
        func getCount() -> Int {
            lock.lock()
            defer { lock.unlock() }
            return count
        }
    }
    
  2. NSRecursiveLock: Επιτρέπει επανειλημτικό κλείδωμα από την ίδια νηματική διαδικασία.

    import Foundation
    
    class RecursiveLocker {
        private let lock = NSRecursiveLock()
    
        func firstMethod() {
            lock.lock()
            defer { lock.unlock() }
            print("Πρώτη μέθοδος")
            secondMethod() // Επαναλαμβανόμενο κλείδωμα είναι εφικτό
        }
    
        func secondMethod() {
            lock.lock()
            defer { lock.unlock() }
            print("Δεύτερη μέθοδος")
        }
    }
    
  3. NSCondition / NSConditionLock: Επιτρέπει στα νήματα να περιμένουν μια συγκεκριμένη συνθήκη (Condition) ή τιμή (ConditionLock) πριν συνεχίσουν την εκτέλεση. Χρησιμοποιείται για την υλοποίηση του προτύπου 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() // Περιμένει μέχρι να υπάρξει ελεύθερος χώρος
            }
    
            items.append(item)
            print("Προστέθηκε: \(item), Τρέχων buffer: \(items)")
            condition.signal() // Σηματοδοτεί τα νήματα που περιμένουν
        }
    
        func removeItem() -> Int {
            condition.lock()
            defer { condition.unlock() }
    
            while items.isEmpty {
                condition.wait() // Περιμένει μέχρι να εμφανιστεί ένα στοιχείο
            }
    
            let item = items.removeFirst()
            print("Αφαιρέθηκε: \(item), Τρέχων buffer: \(items)")
            condition.signal() // Σηματοδοτεί τα νήματα που περιμένουν χώρο
            return item
        }
    }
    
  4. Grand Central Dispatch (GCD):

    • Serial Queues: Εκτελούν εργασίες διαδοχικά, μία κάθε φορά σε μια συγκεκριμένη νηματική διαδικασία, διασφαλίζοντας ατομική πρόσβαση στους πόρους.

    • Concurrent Queues με Barrier Flags: Επιτρέπουν την ανάγνωση ταυτόχρονα, αλλά οι εγγραφές (write) εκτελούνται αποκλειστικά. Αυτό υλοποιεί το μοντέλο read-write lock.

      import Foundation
      
      class ThreadSafeArray<Element> {
          private var array: [Element] = []
          private let queue =
          DispatchQueue(label: "com.mycompany.threadsafearray", attributes: .concurrent) // Ταυτόχρονη ουρά
      
          func append(_ newElement: Element) {
              queue.async(flags: .barrier) { // Barrier για εγγραφή
                  self.array.append(newElement)
              }
          }
      
          func get(at index: Int) -> Element? {
              var result: Element?
              queue.sync { // Συγχρονισμός για ανάγνωση (ή async, αν η σειρά των αναγνώσεων δεν είναι σημαντική)
                  if index < self.array.count {
                      result = self.array[index]
                  }
              }
              return result
          }
      }
      
  5. Ενέργειες Context Core Data: Κάθε NSManagedObjectContext έχει το δικό του ιδιωτικό σειριακό νήμα. Η πρόσβαση σε αντικείμενα και το context πρέπει να γίνεται μόνο από αυτό το νήμα.

    import CoreData
    
    // Παράδειγμα αποθήκευσης σε context Core Data
    let context = // Λήψη context
    context.perform { // Εκτέλεση στο ιδιωτικό νήμα του context
        // Επεξεργασία αντικειμένων Core Data εδώ
        do {
            try context.save()
        } catch {
            // Διαχείριση σφάλματος
        }
    }
    

Η επιλογή της μεθόδου εξαρτάται από την συγκεκριμένη εργασία και τις απαιτήσεις απόδοσης. Το GCD με σειριακές ή ταυτόχρονες ουρές με barriers συχνά είναι η πιο προτιμώμενη προσέγγιση λόγω της αποτελεσματικότητας και της ευκολίας χρήσης. Για το Core Data, βεβαιωθείτε πάντα ότι χρησιμοποιείτε τις μεθόδους perform ή performAndWait.