Sobes.tech
Middle+

Kako sinhronizovati podatke između niti i obezbediti konzistentnost konteksta?

sobes.tech АИ

Одговор од АИ

Za sinhronizaciju podataka i obezbeđivanje konzistentnosti konteksta u višestrukom nitnom okruženju na iOS-u koriste se sledeći pristupi:

  1. NSLock / Lock: Jednostavni mutexi za blokiranje kritičnih sekcija koda.

    import Foundation
    
    class ThreadSafeCounter {
        private var count: Int = 0
        private let lock = NSLock()
    
        func increment() {
            lock.lock()
            defer { lock.unlock() } // Garantovano otpuštanje blokade
            count += 1
        }
    
        func getCount() -> Int {
            lock.lock()
            defer { lock.unlock() }
            return count
        }
    }
    
  2. NSRecursiveLock: Omogućava višestruko zaključavanje iz istog niti.

    import Foundation
    
    class RecursiveLocker {
        private let lock = NSRecursiveLock()
    
        func firstMethod() {
            lock.lock()
            defer { lock.unlock() }
            print("Prva metoda")
            secondMethod() // Moguće je višestruko zaključavanje
        }
    
        func secondMethod() {
            lock.lock()
            defer { lock.unlock() }
            print("Druga metoda")
        }
    }
    
  3. NSCondition / NSConditionLock: Omogućava nitima da čekaju određeni uslov (Condition) ili vrednost (ConditionLock) pre nastavka izvršavanja. Koristi se za implementaciju producer-consumer obrasca.

    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() // Čeka dok se ne oslobodi mesto
            }
    
            items.append(item)
            print("Dodat: \(item), Trenutni buffer: \(items)")
            condition.signal() // Signalizira čekajućim nitima
        }
    
        func removeItem() -> Int {
            condition.lock()
            defer { condition.unlock() }
    
            while items.isEmpty {
                condition.wait() // Čeka dok se ne pojavi element
            }
    
            let item = items.removeFirst()
            print("Uklonjen: \(item), Trenutni buffer: \(items)")
            condition.signal() // Signalizira čekajućim nitima
            return item
        }
    }
    
  4. Grand Central Dispatch (GCD):

    • Serial Queues: Izvršavaju zadatke redom, jedan po jedan na datom thread-u, što obezbeđuje atomarni pristup resursima.

    • Concurrent Queues sa Barrier Flags: Omogućava izvršavanje čitanja paralelno, ali operacije pisanja (write) se izvršavaju isključivo. Ovo implementira model read-write lock.

      import Foundation
      
      class ThreadSafeArray<Element> {
          private var array: [Element] = []
          private let queue =
          DispatchQueue(label: "com.mycompany.threadsafearray", attributes: .concurrent) // Konkurentna red
      
          func append(_ newElement: Element) {
              queue.async(flags: .barrier) { // Barrier za pisanje
                  self.array.append(newElement)
              }
          }
      
          func get(at index: Int) -> Element? {
              var result: Element?
              queue.sync { // Sync za čitanje (ili async, ako redosled čitanja nije važan)
                  if index < self.array.count {
                      result = self.array[index]
                  }
              }
              return result
          }
      }
      
  5. Operacije Core Data Contexta: Svaki NSManagedObjectContext ima svoj sopstveni privatni serijalni thread. Pristup objektima i kontekstu treba vršiti samo sa tog thread-a.

    import CoreData
    
    // Primer čuvanja u Core Data kontekstu
    let context = // Dobijeni kontekst
    context.perform { // Izvršavanje na privatnom thread-u konteksta
        // Rad sa Core Data objektima
        do {
            try context.save()
        } catch {
            // Obrada greške
        }
    }
    

Izbor metode zavisi od konkretne zadatka i zahteva za performanse. GCD sa serijalnim ili paralelnim redovima sa barijerama je često najpreferiraniji pristup zbog svoje efikasnosti i jednostavnosti korišćenja. Za Core Data, uvek koristite metode perform ili performAndWait.