Thread Safety para un getter y setter en singleton

He creado un simple singleton en Swift 3:

class MySingleton { private var myName: String private init() {} static let shanetworking = MySingleton() func setName(_ name: String) { myName = name } func getName() -> String { return myName } } 

Desde que hice el init() privado, y también declaré que la instancia shanetworking era static let , creo que el inicializador es seguro para subprocesss. Pero, ¿qué pasa con las funciones getter y setter para myName , son seguras para subprocesss?

Está en lo cierto que los getters que ha escrito no son seguros para subprocesss. En Swift, la manera más simple (leer lo más seguro) para lograr esto en este momento es usar las queues de Grand Central Dispatch como un mecanismo de locking. La manera más simple (y más fácil de razonar) para lograr esto es con una queue serie básica.

 class MySingleton { static let shanetworking = MySingleton() // Serial dispatch queue private let lockQueue = DispatchQueue(label: "MySingleton.lockQueue") private var _myName: String var myName: String { get { var result: String! lockQueue.sync { result = self._myName } return result } set { lockQueue.sync { self._myName = newValue } } } private init() { _myName = "initial name" } } 

El uso de una queue de envío en serie garantizará la primera input, la primera ejecución y la "cerradura" de los datos. Es decir, los datos no se pueden leer mientras se está cambiando. En este enfoque, usamos la sync para ejecutar las lecturas y escrituras reales de los datos, lo que significa que la persona que llama siempre estará obligada a esperar su turno, similar a otras primitivas de locking.

Nota: este no es el enfoque más dynamic, pero es fácil de leer y comprender. Es una buena solución de propósito general para evitar las condiciones de carrera, pero no pretende proporcionar la synchronization para el desarrollo de algorithms paralelos.

Fuentes: https://mikeash.com/pyblog/friday-qa-2015-02-06-locks-thread-safety-and-swift.html ¿Qué es Swift equivalente a Objective-C's "@synchronized"?

Una forma ligeramente diferente de hacerlo (y esto es desde un Xcode 9 Playground) es utilizar una queue simultánea en lugar de una queue de serie.

 final class MySingleton { static let shanetworking = MySingleton() private let nameQueue = DispatchQueue(label: "name.accessor", qos: .default, attributes: .concurrent) private var _name = "Initial name" private init() {} var name: String { get { var name = "" nameQueue.sync { name = _name } return name } set { nameQueue.async(flags: .barrier) { self._name = newValue } } } } 
  • El uso de una queue simultánea significa que múltiples lecturas de varios subprocesss no se bloquean entre sí. Dado que no hay mutación en la obtención, el valor se puede leer simultáneamente, porque …
  • Estamos configurando los nuevos valores utilizando un .barrier asynchronous de .barrier . El locking se puede realizar de forma asíncrona porque no es necesario que el llamante espere a que se establezca el valor. El bloque no se ejecutará hasta que se hayan completado todos los demás bloques en la queue simultánea anterior. Por lo tanto, las lecturas pendientes existentes no se verán afectadas mientras este configurador esté esperando a ejecutarse. La barrera significa que cuando comience a ejecutarse, no se ejecutarán otros bloques. Efectivamente, convertir la queue en una queue de serie durante la duración del set. No se pueden hacer más lecturas hasta que este bloque se complete. Cuando el bloque ha finalizado, se ha configurado el nuevo valor, cualquier captador agregado después de este configurador ahora puede ejecutarse simultáneamente.