Sobes.tech
Middle+

Как да синхронизираме данните между нишки и да осигурим съгласуваност на контекста?

sobes.tech AI

Отговор от AI

За синхронизиране на данните и осигуряване на съгласуваност на контекста в многонишково приложение на iOS се използват следните подходи:

  1. NSLock / Lock: Простички мьютекси за блокиране на критични секции от код.

    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), Текущ буфер: \(items)")
            condition.signal() // Сигнализира чакащите нишки
        }
    
        func removeItem() -> Int {
            condition.lock()
            defer { condition.unlock() }
    
            while items.isEmpty {
                condition.wait() // Чака, докато се появи елемент
            }
    
            let item = items.removeFirst()
            print("Премахнато: \(item), Текущ буфер: \(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) { // Бариера за писане
                  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. Core Data Context операции: Всеки NSManagedObjectContext има свой собствен частен сериален поток. Достъпът до обектите и контекста трябва да се извършва само от този поток.

    import CoreData
    
    // Пример за запазване в контекста на Core Data
    let context = // Получен контекст
    context.perform { // Изпълнение на приватен поток на контекста
        // Работа с обекти на Core Data тук
        do {
            try context.save()
        } catch {
            // Обработка на грешка
        }
    }
    

Изборът на метод зависи от конкретната задача и изискванията за производителност. GCD с серийни опашки или паралелни опашки с бариери често е най-препоръчителният подход поради ефективността и удобството при използване. За Core Data винаги използвайте методите perform или performAndWait.