Crear matriz segura de subprocesss en swift

Tengo un problema de subprocesss rápido. Tengo una matriz con algunos objects en ella. Sobre un delegado, la class obtiene nuevos objects cada segundo. Después de eso, tengo que comprobar si los objects ya están en la matriz, así que tengo que actualizar el object, de lo contrario, tengo que eliminar / agregar el nuevo object.

Si agrego un object nuevo, primero tengo que search algunos datos en la networking. Esto se realiza a través de un bloque.

Ahora mi problema es, ¿cómo puedo sincronizar estas tareas?

He intentado un dispatch_semaphore, pero este bloquea la interfaz de usuario, hasta que el bloque finaliza.

También he intentado una simple variable bool, que comtesting si el bloque se ejecuta actualmente y salta el método de comparación mientras tanto.

Pero ambos methods no son ideales.

Cuál es la mejor manera de administrar la matriz, no quiero tener datos duplicates en la matriz.

Kirsteins está en lo correcto, pero no siempre necesitas usar la queue de despacho. Puedes usar:

objc_sync_enter(array) // manipulate the array objc_sync_exit(array) 

Esto debería hacer el truco. Para una bonificación adicional, puede crear una function para usar siempre que necesite security de subprocesss:

 func sync(lock: NSObject, closure: () -> Void) { objc_sync_enter(lock) closure() objc_sync_exit(lock) } ... var list = NSMutableArray() sync (list) { list.addObject("something") } 

Tenga en count que he cambiado AnyObject a NSObject . En Swift, los types de collections se implementan como struct y se pasan por valor , por lo que supongo que sería más seguro trabajar con classs de recostackción mutables que se pasan por reference cuando se utiliza la conveniente function de sync .

Mi enfoque de este problema estaba utilizando la queue de envío en serie, para sincronizar el acceso a la matriz en caja. Bloqueará el subprocess cuando intentes get el valor en el índice y la queue está realmente ocupada, pero ese es el problema con los lockings también.

 public class SynchronizedArray<T> { private var array: [T] = [] private let accessQueue = dispatch_queue_create("SynchronizedArrayAccess", DISPATCH_QUEUE_SERIAL) public func append(newElement: T) { dispatch_async(self.accessQueue) { self.array.append(newElement) } } public subscript(index: Int) -> T { set { dispatch_async(self.accessQueue) { self.array[index] = newValue } } get { var element: T! dispatch_sync(self.accessQueue) { element = self.array[index] } return element } } } var a = SynchronizedArray<Int>() a.append(1) a.append(2) a.append(3) // can be empty as this is non-thread safe access println(a.array) // thread-safe synchonized access println(a[0]) println(a[1]) println(a[2]) 

La respuesta de Kirsteins es correcta, pero por conveniencia, he actualizado esa respuesta con las sugerencias de Amol Chaudhari y Rob para usar una queue simultánea con barrera asíncrona para permitir lecturas simultáneas, pero bloquear las escrituras.

También he envuelto algunas otras funciones de matriz que fueron útiles para mí.

 public class SynchronizedArray<T> { private var array: [T] = [] private let accessQueue = dispatch_queue_create("SynchronizedArrayAccess", DISPATCH_QUEUE_CONCURRENT) public func append(newElement: T) { dispatch_barrier_async(self.accessQueue) { self.array.append(newElement) } } public func removeAtIndex(index: Int) { dispatch_barrier_async(self.accessQueue) { self.array.removeAtIndex(index) } } public var count: Int { var count = 0 dispatch_sync(self.accessQueue) { count = self.array.count } return count } public func first() -> T? { var element: T? dispatch_sync(self.accessQueue) { if !self.array.isEmpty { element = self.array[0] } } return element } public subscript(index: Int) -> T { set { dispatch_barrier_async(self.accessQueue) { self.array[index] = newValue } } get { var element: T! dispatch_sync(self.accessQueue) { element = self.array[index] } return element } } } 

ACTUALIZACIÓN Este es el mismo código, actualizado para Swift3.

 public class SynchronizedArray<T> { private var array: [T] = [] private let accessQueue = DispatchQueue(label: "SynchronizedArrayAccess", attributes: .concurrent) public func append(newElement: T) { self.accessQueue.async(flags:.barrier) { self.array.append(newElement) } } public func removeAtIndex(index: Int) { self.accessQueue.async(flags:.barrier) { self.array.remove(at: index) } } public var count: Int { var count = 0 self.accessQueue.sync { count = self.array.count } return count } public func first() -> T? { var element: T? self.accessQueue.sync { if !self.array.isEmpty { element = self.array[0] } } return element } public subscript(index: Int) -> T { set { self.accessQueue.async(flags:.barrier) { self.array[index] = newValue } } get { var element: T! self.accessQueue.sync { element = self.array[index] } return element } } } 

Un detalle menor: en Swift 3 (al less en XCode 8 Beta 6), la syntax para las queues cambió significativamente. Los cambios importantes a la respuesta de @Kirsteins serán:

 private let accessQueue = DispatchQueue(label: "SynchronizedArrayAccess") txAccessQueue.async() { // Your async code goes here... } txAccessQueue.sync() { // Your sync code goes here... } 

Creo que vale la pena mirar a dispatch_barriers. Usar gcd para la sincronicidad es más intuitivo que usar palabras key de synchronization para evitar la mutación de estado de múltiples subprocesss.

https://mikeash.com/pyblog/friday-qa-2011-10-14-whats-new-in-gcd.html

Aquí hay una gran respuesta que es thread safe y no bloquea las lecturas simultáneas: https://stackoverflow.com/a/15936959/2050665

Está escrito en el Objetivo C, pero la transferencia a Swift es trivial.

 @property (nonatomic, readwrite, strong) dispatch_queue_t thingQueue; @property (nonatomic, strong) NSObject *thing; - (id)init { ... _thingQueue = dispatch_queue_create("...", DISPATCH_QUEUE_CONCURRENT); ... } - (NSObject *)thing { __block NSObject *thing; dispatch_sync(self.thingQueue, ^{ thing = _thing; }); return thing; } - (void)setThing:(NSObject *)thing { dispatch_barrier_async(self.thingQueue, ^{ _thing = thing; }); } 

Crédito a https://stackoverflow.com/users/97337/rob-napier

Use DispatchQueue para sincronizar

Código:

 class SomeClass { private let queue = DispatchQueue(label: "synchronize") //Not thread-safe private var _array : [Int]? //Thread-safe var array : [Int]? { get { return queue.sync { _array } } set { queue.sync { _array = newValue } } } }