objective-c - programar - swift(lenguaje de programación)
¿Las variables de Swift son atómicas? (4)
Detalles
xCode 9.1, Swift 4
Campo de golf
- Despacho de apple.developer.com
- Grand Central Dispatch (GCD) y despacho de colas en Swift 3
- Crear matrices Thread-Safe en Swift
- Mutexes y captura de cierre en Swift
Muestra de acceso atómico
class Atomic {
let dispatchGroup = DispatchGroup()
private var variable = 0
// Usage of semaphores
func semaphoreSample() {
// value: 1 - number of threads that have simultaneous access to the variable
let atomicSemaphore = DispatchSemaphore(value: 1)
variable = 0
runInSeveralQueues { dispatchQueue in
// Only (value) queqes can run operations betwen atomicSemaphore.wait() and atomicSemaphore.signal()
// Others queues await their turn
atomicSemaphore.wait() // Lock access until atomicSemaphore.signal()
self.variable += 1
print("/(dispatchQueue), value: /(self.variable)")
atomicSemaphore.signal() // Unlock access
}
notifyWhenDone {
atomicSemaphore.wait() // Lock access until atomicSemaphore.signal()
print("variable = /(self.variable)")
atomicSemaphore.signal() // Unlock access
}
}
// Usage of sync of DispatchQueue
func dispatchQueueSync() {
let atomicQueue = DispatchQueue(label: "dispatchQueueSync")
variable = 0
runInSeveralQueues { dispatchQueue in
// Only queqe can run this closure (atomicQueue.sync {...})
// Others queues await their turn
atomicQueue.sync {
self.variable += 1
print("/(dispatchQueue), value: /(self.variable)")
}
}
notifyWhenDone {
atomicQueue.sync {
print("variable = /(self.variable)")
}
}
}
// Usage of objc_sync_enter/objc_sync_exit
func objcSync() {
variable = 0
runInSeveralQueues { dispatchQueue in
// Only one queqe can run operations betwen objc_sync_enter(self) and objc_sync_exit(self)
// Others queues await their turn
objc_sync_enter(self) // Lock access until objc_sync_exit(self).
self.variable += 1
print("/(dispatchQueue), value: /(self.variable)")
objc_sync_exit(self) // Unlock access
}
notifyWhenDone {
objc_sync_enter(self) // Lock access until objc_sync_exit(self)
print("variable = /(self.variable)")
objc_sync_exit(self) // Unlock access
}
}
}
// Helpers
extension Atomic {
fileprivate func notifyWhenDone(closure: @escaping ()->()) {
dispatchGroup.notify(queue: .global(qos: .utility)) {
closure()
print("All work done")
}
}
fileprivate func runInSeveralQueues(closure: @escaping (DispatchQueue)->()) {
async(dispatch: .main, closure: closure)
async(dispatch: .global(qos: .userInitiated), closure: closure)
async(dispatch: .global(qos: .utility), closure: closure)
async(dispatch: .global(qos: .default), closure: closure)
async(dispatch: .global(qos: .userInteractive), closure: closure)
}
private func async(dispatch: DispatchQueue, closure: @escaping (DispatchQueue)->()) {
for _ in 0 ..< 100 {
dispatchGroup.enter()
dispatch.async {
let usec = Int(arc4random()) % 100_000
usleep(useconds_t(usec))
closure(dispatch)
self.dispatchGroup.leave()
}
}
}
}
Uso
Atomic().semaphoreSample()
//Atomic().dispatchQueueSync()
//Atomic().objcSync()
Resultado
En Objective-C tiene una distinción entre propiedades atómicas y no atómicas:
@property (nonatomic, strong) NSObject *nonatomicObject;
@property (atomic, strong) NSObject *atomicObject;
Desde mi entender, puede leer y escribir propiedades definidas como atómicas a partir de múltiples hilos de forma segura, mientras que escribir y acceder a propiedades no ativas o ivars de múltiples hilos al mismo tiempo puede dar como resultado un comportamiento indefinido, incluidos errores de acceso incorrectos.
Entonces, si tienes una variable como esta en Swift:
var object: NSObject
¿Puedo leer y escribir en esta variable en paralelo de forma segura? (Sin considerar el significado real de hacer esto).
Es muy pronto para suponer que no hay documentación de bajo nivel disponible, pero puede estudiar desde el ensamblaje. Hopper Disassembler es una gran herramienta.
@interface ObjectiveCar : NSObject
@property (nonatomic, strong) id engine;
@property (atomic, strong) id driver;
@end
Utiliza objc_storeStrong
y objc_setProperty_atomic
para nonatomic y atomic respectivamente, donde
class SwiftCar {
var engine : AnyObject?
init() {
}
}
usa swift_retain
de libswift_stdlib_core
y, al parecer, no tiene integrada la seguridad de subprocesos.
Podemos especular que palabras clave adicionales (similar a @lazy
) podrían ser introducidas más adelante.
Actualización 20/07/15 : de acuerdo con este blogpost en singletons swift environment puede hacer que ciertos casos sean seguros para usted, es decir:
class Car {
static let sharedCar: Car = Car() // will be called inside of dispatch_once
}
private let sharedCar: Car2 = Car2() // same here
class Car2 {
}
Actualización 25/05/16 : esté atento a la rápida propuesta de evolución https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md - parece que es va a ser posible tener el comportamiento @atomic
implementado por usted mismo.
Probablemente sea temprano para responder a esta pregunta. Actualmente, Swift carece de modificadores de acceso, por lo que no hay una forma obvia de agregar código que administre la concurrencia alrededor de un getter / setter de propiedades. Además, Swift Language aún no parece tener información sobre la simultaneidad. (También carece de KVO, etc.)
Creo que la respuesta a esta pregunta quedará clara en futuras versiones.
Swift no tiene construcciones de lenguaje en torno a la seguridad del hilo. Se supone que utilizará las bibliotecas proporcionadas para realizar su propia administración de seguridad de subprocesos. Hay una gran cantidad de opciones que tiene para implementar la seguridad de subprocesos, incluidos pthread mutexes, NSLock y dispatch_sync como mecanismo de exclusión mutua. Ver la reciente publicación de Mike Ash sobre el tema: https://mikeash.com/pyblog/friday-qa-2015-02-06-locks-thread-safety-and-swift.html Así que la respuesta directa a su pregunta de "Can Leo y escribo esta variable en paralelo de forma segura? " no es.