Are Swift variables atomic?
在Objective-C中,原子和非原子性质之间存在区别:
1 2 | @property (nonatomic, strong) NSObject *nonatomicObject; @property (atomic, strong) NSObject *atomicObject; |
据我所知,您可以安全地从多个线程读取和写入定义为原子的属性,而同时从多个线程写入和访问非原子属性或ivar可能会导致未定义的行为,包括错误的访问错误。
所以如果在swift中有这样的变量:
1 | var object: NSObject |
我可以安全地并行读写这个变量吗?(不考虑这样做的实际意义)。
由于没有可用的低级文档,所以假设这一点很早,但您可以从组装中学习。漏斗拆卸器是一个很好的工具。
1 2 3 4 | @interface ObjectiveCar : NSObject @property (nonatomic, strong) id engine; @property (atomic, strong) id driver; @end |
分别将
1 2 3 4 5 | class SwiftCar { var engine : AnyObject? init() { } } |
使用
我们可以推测,以后可能会引入其他关键字(类似于
更新日期:2015年7月20日:根据这个关于singletons的blogpost,swift环境可以使某些情况对您来说是线程安全的,即:
1 2 3 4 5 6 7 8 | class Car { static let sharedCar: Car = Car() // will be called inside of dispatch_once } private let sharedCar: Car2 = Car2() // same here class Car2 { } |
更新日期:2016年5月25日:密切关注Swift Evolution Proposal https://github.com/apple/Swift-Evolution/blob/master/proposals/0030-property-behavior-decls.md-看起来您可以自己实施
Swift没有关于线程安全的语言结构。假设您将使用提供的库来进行您自己的线程安全管理。在实现线程安全方面有大量的选项,包括pthread mutex、nslock和作为mutex机制的调度同步。参见Mike Ash最近关于这个主题的帖子:https://mike ash.com/pyblog/friday-qa-2015-02-06-locks-thread-safety-and-swift.html所以直接回答你的问题"我能安全地读写这个变量吗?"不是。
回答这个问题可能还为时过早。目前Swift缺少访问修饰符,因此没有明显的方法来添加代码来管理getter/setter属性周围的并发性。此外,Swift语言似乎还没有关于并发性的任何信息!(它还缺少kvo等…)
我认为这个问题的答案将在未来的版本中变得清晰。
细节
- 代码9.1,Swift 4
- Xcode 10.2.1(10E1001),Swift 5
链接
- apple.developer.com发送
- 中央调度中心(GCD)和swift 3中的调度队列
- 在中创建线程安全数组迅捷
- Mutexes与斯威夫特的封闭俘虏
实现类型
- 原子阵列
- 原子数
主要思想
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class Example { private lazy var semaphore = DispatchSemaphore(value: 1) func executeThreadSafeFunc1() { // Lock access. Only first thread can execute code below. // Other threads will wait until semaphore.signal() will execute semaphore.wait() // your code semaphore.signal() // Unlock access } func executeThreadSafeFunc2() { // Lock access. Only first thread can execute code below. // Other threads will wait until semaphore.signal() will execute semaphore.wait() DispatchQueue.global(qos: .background).async { // your code self.semaphore.signal() // Unlock access } } } |
原子访问示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | 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() } } } } |
用法
1 2 3 | Atomic().semaphoreSample() //Atomic().dispatchQueueSync() //Atomic().objcSync() |
结果