Les variables Swift sont-elles atomiques?

En Objective-C, vous faites une distinction entre les propriétés atomiques et non-atomiques:

@property (nonatomic, strong) NSObject *nonatomicObject; @property (atomic, strong) NSObject *atomicObject; 

À ma connaissance, vous pouvez lire et écrire des propriétés définies comme atomiques à partir de plusieurs threads en toute sécurité, tandis que l’écriture et l’access simultanés à des propriétés non atomiques ou à des ivars à partir de plusieurs threads peuvent entraîner un comportement indéfini, y compris de mauvaises erreurs d’access.

Donc, si vous avez une variable comme celle-ci dans Swift:

 var object: NSObject 

Puis-je lire et écrire sur cette variable en parallèle en toute sécurité? (Sans tenir compte de la signification réelle de cela).

Il est très tôt pour supposer qu’aucune documentation de bas niveau n’est disponible, mais vous pouvez étudier à partir de l’assemblage. Hopper Disassembler est un excellent outil.

 @interface ObjectiveCar : NSObject @property (nonatomic, strong) id engine; @property (atomic, strong) id driver; @end 

Utilise objc_storeStrong et objc_setProperty_atomic pour nonatomic et atomic respectivement, où

 class SwiftCar { var engine : AnyObject? init() { } } 

utilise swift_retain de libswift_stdlib_core et, apparemment, n’a pas de sécurité de thread intégrée.

Nous pouvons @lazy que des mots-clés supplémentaires (similaires à @lazy ) pourraient être introduits plus tard.

Mise à jour 20/07/15 : selon cet article de blog sur les singletons, swift environment peut rendre certains dossiers sûrs pour vous, à savoir:

 class Car { static let sharedCar: Car = Car() // will be called inside of dispatch_once } private let sharedCar: Car2 = Car2() // same here class Car2 { } 

Mise à jour 25/05/16 : Gardez un œil sur la proposition d’évolution rapide https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md – il semble que ce soit va être possible d’avoir @atomic comportement @atomic mis en œuvre par vous-même.

Swift n’a aucun langage construit autour de la sécurité des threads. Il est supposé que vous utiliserez les bibliothèques fournies pour effectuer votre propre gestion de la sécurité des threads. Il existe un grand nombre d’options pour implémenter la sécurité des threads, y compris les mutex pthread, NSLock et dispatch_sync en tant que mécanisme de mutex. Voir le post récent de Mike Ash sur le sujet: https://mikeash.com/pyblog/friday-qa-2015-02-06-locks-thread-safety-and-swift.html Donc, la réponse directe à votre question de “Can Je lis et écris en parallèle cette variable en toute sécurité? ” c’est non

Il est probablement trop tôt pour répondre à cette question. Actuellement, swift manque de modificateurs d’access, il n’ya donc pas de moyen évident d’append du code qui gère la concurrence autour d’un getter / setter de propriétés. De plus, le langage rapide ne semble pas avoir d’informations sur la concurrence! (Il manque aussi du KVO etc …)

Je pense que la réponse à cette question deviendra claire dans les prochaines versions.

Détails

Xcode 9.1, Swift 4

Liens

  • apple.developer.com Expédition
  • Grand Central Dispatch (GCD) et files d’attente de répartition dans Swift 3
  • Création de tableaux thread-safe dans Swift
  • Mutexes et capture de fermeture à Swift

Échantillon d’access atomique

 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() } } } } 

Usage

 Atomic().semaphoreSample() //Atomic().dispatchQueueSync() //Atomic().objcSync() 

Résultat

entrer la description de l'image ici