objective-c Swift 变量是原子的吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24157834/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Are Swift variables atomic?
提问by lassej
In Objective-C you have a distinction between atomic and nonatomic properties:
在 Objective-C 中,您可以区分原子属性和非原子属性:
@property (nonatomic, strong) NSObject *nonatomicObject;
@property (atomic, strong) NSObject *atomicObject;
From my understanding you can read and write properties defined as atomic from multiple threads safely, while writing and accessing nonatomic properties or ivars from multiple threads at the same time can result in undefined behavior, including bad access errors.
根据我的理解,您可以安全地从多个线程读取和写入定义为原子的属性,而同时从多个线程写入和访问非原子属性或 ivars 可能会导致未定义的行为,包括错误访问错误。
So if you have a variable like this in Swift:
因此,如果您在 Swift 中有这样的变量:
var object: NSObject
Can I read and write to this variable in parallel safely? (Without considering the actual meaning of doing this).
我可以安全地并行读写这个变量吗?(不考虑这样做的实际意义)。
采纳答案by Sash Zats
It's very early to assume as no low-level documentation is available, but you can study from assembly. Hopper Disassembleris a great tool.
现在假设没有可用的低级文档还为时过早,但您可以从汇编中学习。Hopper Disassembler是一个很棒的工具。
@interface ObjectiveCar : NSObject
@property (nonatomic, strong) id engine;
@property (atomic, strong) id driver;
@end
Uses objc_storeStrongand objc_setProperty_atomicfor nonatomic and atomic respectively, where
用途objc_storeStrong和objc_setProperty_atomic用于非原子和原子分别,其中
class SwiftCar {
var engine : AnyObject?
init() {
}
}
uses swift_retainfrom libswift_stdlib_coreand, apparently, does not have thread safety built in.
使用swift_retainfromlibswift_stdlib_core并且显然没有内置线程安全。
We can speculate that additional keywords (similar to @lazy) might be introduced later on.
我们可以推测@lazy稍后可能会引入其他关键字(类似于)。
Update 07/20/15: according to this blogpost on singletonsswift environment can make certain cases thread safe for you, i.e.:
2015 年 7 月 20 日更新:根据这篇关于单身人士swift 环境的博客文章可以使某些情况对您来说是线程安全的,即:
class Car {
static let sharedCar: Car = Car() // will be called inside of dispatch_once
}
private let sharedCar: Car2 = Car2() // same here
class Car2 {
}
Update 05/25/16: Keep an eye out for swift evolution proposal https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md- it looks like it is going to be possible to have @atomicbehavior implemented by yourself.
2016 年 5 月 25 日更新:留意快速进化提案https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md- 看起来是这样将有可能让@atomic自己实现行为。
回答by Good Doug
Swift has no language constructs around thread safety. It is assumed that you will be using the provided libraries to do your own thread safety management. There are a large number of options you have in implementing thread safety including pthread mutexes, NSLock, and dispatch_sync as a mutex mechanism. See Mike Ash's recent post on the subject: https://mikeash.com/pyblog/friday-qa-2015-02-06-locks-thread-safety-and-swift.htmlSo the direct answer to your question of "Can I read and write to this variable in parallel safely?" is No.
Swift 没有围绕线程安全的语言结构。假设您将使用提供的库来进行您自己的线程安全管理。在实现线程安全方面有很多选择,包括 pthread 互斥锁、NSLock 和作为互斥锁机制的 dispatch_sync。请参阅 Mike Ash 最近关于该主题的帖子:https: //mikeash.com/pyblog/friday-qa-2015-02-06-locks-thread-safety-and-swift.html所以直接回答您的问题“可以我可以安全地并行读写这个变量吗?” 没有。
回答by ColinE
It is probably to early to answer this question. Currently swift lacks access modifiers, so there is not obvious way to add code which manages concurrency around a properties getter / setter. Furthermore, the Swift Language doesn't seem to have any information about concurrency yet! (It also lacks KVO etc ...)
现在回答这个问题可能还为时过早。目前 swift 缺少访问修饰符,因此没有明显的方法来添加代码来管理围绕属性 getter/setter 的并发性。此外,Swift 语言似乎还没有关于并发的任何信息!(它也缺少 KVO 等...)
I think the answer to this question will become clear in future releases.
我认为这个问题的答案将在未来的版本中变得清晰。
回答by Vasily Bodnarchuk
Details
细节
- Xcode 9.1, Swift 4
- Xcode 10.2.1 (10E1001), Swift 5
- Xcode 9.1,斯威夫特 4
- Xcode 10.2.1 (10E1001),Swift 5
Links
链接
- apple.developer.com Dispatch
- Grand Central Dispatch (GCD) and Dispatch Queues in Swift 3
- Creating Thread-Safe Arrays in Swift
- Mutexes and closure capture in Swift
- apple.developer.com 调度
- Swift 3 中的 Grand Central Dispatch (GCD) 和 Dispatch Queues
- 在 Swift 中创建线程安全数组
- Swift 中的互斥量和闭包捕获
Implemented types
实现类型
Main Idea
大意
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
}
}
}
Sample of atomic access
原子访问示例
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()
Result
结果
回答by iUrii
From Swift 5.1 you can use property wrappersto make specific logic for your properties. This is atomic wrapper implementation:
从 Swift 5.1 开始,您可以使用属性包装器为您的属性制作特定的逻辑。这是原子包装器实现:
@propertyWrapper
struct atomic<T> {
private var value: T
private let lock = NSLock()
init(wrappedValue value: T) {
self.value = value
}
var wrappedValue: T {
get { getValue() }
set { setValue(newValue: newValue) }
}
func getValue() -> T {
lock.lock()
defer { lock.unlock() }
return value
}
mutating func setValue(newValue: T) {
lock.lock()
defer { lock.unlock() }
value = newValue
}
}
How to use:
如何使用:
class Shared {
@atomic var value: Int
...
}
回答by jamone
Here is the atomic property wrapper that I use extensively. I made the actual locking mechanism a protocol, so I could experiement with different mechanisms. I tried semaphores, DispatchQueues, and the pthread_rwlock_t. The pthread_rwlock_twas chosen because it appears to have the lowest overhead, and a lower chance of a priority inversion.
这是我广泛使用的原子属性包装器。我把实际的锁定机制变成了一个协议,所以我可以尝试不同的机制。我尝试了信号量DispatchQueues、 和pthread_rwlock_t。pthread_rwlock_t之所以选择,是因为它似乎具有最低的开销,并且发生优先级反转的可能性较低。
/// Defines a basic signature that all locks will conform to. Provides the basis for atomic access to stuff.
protocol Lock {
init()
/// Lock a resource for writing. So only one thing can write, and nothing else can read or write.
func writeLock()
/// Lock a resource for reading. Other things can also lock for reading at the same time, but nothing else can write at that time.
func readLock()
/// Unlock a resource
func unlock()
}
final class PThreadRWLock: Lock {
private var rwLock = pthread_rwlock_t()
init() {
guard pthread_rwlock_init(&rwLock, nil) == 0 else {
preconditionFailure("Unable to initialize the lock")
}
}
deinit {
pthread_rwlock_destroy(&rwLock)
}
func writeLock() {
pthread_rwlock_wrlock(&rwLock)
}
func readLock() {
pthread_rwlock_rdlock(&rwLock)
}
func unlock() {
pthread_rwlock_unlock(&rwLock)
}
}
/// A property wrapper that ensures atomic access to a value. IE only one thing can write at a time.
/// Multiple things can potentially read at the same time, just not during a write.
/// By using `pthread` to do the locking, this safer then using a `DispatchQueue/barrier` as there isn't a chance
/// of priority inversion.
@propertyWrapper
public final class Atomic<Value> {
private var value: Value
private let lock: Lock = PThreadRWLock()
public init(wrappedValue value: Value) {
self.value = value
}
public var wrappedValue: Value {
get {
self.lock.readLock()
defer { self.lock.unlock() }
return self.value
}
set {
self.lock.writeLock()
self.value = newValue
self.lock.unlock()
}
}
/// Provides a closure that will be called synchronously. This closure will be passed in the current value
/// and it is free to modify it. Any modifications will be saved back to the original value.
/// No other reads/writes will be allowed between when the closure is called and it returns.
public func mutate(_ closure: (inout Value) -> Void) {
self.lock.writeLock()
closure(&value)
self.lock.unlock()
}
}


