Sobes.tech
Middle+

¿Cómo sincronizar datos entre hilos y garantizar la coherencia del contexto?

sobes.tech AI

Respuesta de la IA

Para la sincronización de datos y garantizar la coherencia del contexto en una aplicación multihilo en iOS, se utilizan los siguientes enfoques:

  1. NSLock / Lock: Mutex simples para bloquear secciones críticas del código.

    import Foundation
    
    class ThreadSafeCounter {
        private var count: Int = 0
        private let lock = NSLock()
    
        func increment() {
            lock.lock()
            defer { lock.unlock() } // Liberación garantizada del bloqueo
            count += 1
        }
    
        func getCount() -> Int {
            lock.lock()
            defer { lock.unlock() }
            return count
        }
    }
    
  2. NSRecursiveLock: Permite volver a adquirir el bloqueo desde el mismo hilo.

    import Foundation
    
    class RecursiveLocker {
        private let lock = NSRecursiveLock()
    
        func firstMethod() {
            lock.lock()
            defer { lock.unlock() }
            print("Primer método")
            secondMethod() // La adquisición repetida es posible
        }
    
        func secondMethod() {
            lock.lock()
            defer { lock.unlock() }
            print("Segundo método")
        }
    }
    
  3. NSCondition / NSConditionLock: Permiten que los hilos esperen una condición específica o un valor antes de continuar. Se utilizan para implementar el patrón productor-consumidor.

    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() // Espera hasta que haya espacio libre
            }
    
            items.append(item)
            print("Añadido: \(item), Buffer actual: \(items)")
            condition.signal() // Señala a los hilos que esperan
        }
    
        func removeItem() -> Int {
            condition.lock()
            defer { condition.unlock() }
    
            while items.isEmpty {
                condition.wait() // Espera hasta que haya un elemento
            }
    
            let item = items.removeFirst()
            print("Eliminado: \(item), Buffer actual: \(items)")
            condition.signal() // Señala a los hilos que esperan espacio
            return item
        }
    }
    
  4. Grand Central Dispatch (GCD):

    • Colas seriales: Ejecutan tareas de forma secuencial, una a la vez en la misma hebra, garantizando la atomicidad del acceso a los recursos.

    • Colas concurrentes con banderas Barrier: Permiten realizar lecturas concurrentemente, pero las operaciones de escritura (write) se realizan de forma exclusiva. Esto implementa un modelo de bloqueo lectura-escritura.

      import Foundation
      
      class ThreadSafeArray<Element> {
          private var array: [Element] = []
          private let queue =
          DispatchQueue(label: "com.mycompany.threadsafearray", attributes: .concurrent) // Cola concurrente
      
          func append(_ newElement: Element) {
              queue.async(flags: .barrier) { // Barrera para escritura
                  self.array.append(newElement)
              }
          }
      
          func get(at index: Int) -> Element? {
              var result: Element?
              queue.sync { // Sincronización para lectura (o async, si el orden no importa)
                  if index < self.array.count {
                      result = self.array[index]
                  }
              }
              return result
          }
      }
      
  5. Operaciones de Contexto de Core Data: Cada NSManagedObjectContext tiene su propio hilo serial privado. El acceso a objetos y al contexto debe realizarse solo desde ese hilo.

    import CoreData
    
    // Ejemplo de guardado en el contexto de Core Data
    let context = // Contexto obtenido
    context.perform { // Ejecución en el hilo privado del contexto
        // Trabajar con objetos de Core Data aquí
        do {
            try context.save()
        } catch {
            // Manejo del error
        }
    }
    

La elección del método depende de la tarea específica y los requisitos de rendimiento. GCD con colas seriales o colas concurrentes con barreras suele ser la opción más preferida por su eficiencia y facilidad de uso. Para Core Data, siempre utilice los métodos perform o performAndWait.