在Objective-C中,您可以区分原子和非原子属性:
@property (nonatomic, strong) NSObject *nonatomicObject;
@property (atomic, strong) NSObject *atomicObject;
根据我的理解,您可以安全地读取和写入从多个线程定义为原子的属性,而同时从多个线程编写和访问非原子属性或ivars会导致未定义的行为,包括错误的访问错误。
所以如果你在Swift中有这样的变量:
var object: NSObject
我可以安全地并行读取和写入此变量吗? (不考虑这样做的实际意义)。
答案 0 :(得分:50)
现在很早就可以假设没有低级文档可用,但您可以从汇编中学习。 Hopper Disassembler是一个很棒的工具。
@interface ObjectiveCar : NSObject
@property (nonatomic, strong) id engine;
@property (atomic, strong) id driver;
@end
分别对非原子和原子使用objc_storeStrong
和objc_setProperty_atomic
,其中
class SwiftCar {
var engine : AnyObject?
init() {
}
}
使用swift_retain
中的libswift_stdlib_core
,显然没有内置线程安全功能。
我们可以推测,稍后可能会引入其他关键字(类似于@lazy
)。
更新2015年7月20日:根据此blogpost on singletons快速环境,可以使某些案件对您来说是安全的,例如:
class Car {
static let sharedCar: Car = Car() // will be called inside of dispatch_once
}
private let sharedCar: Car2 = Car2() // same here
class Car2 {
}
更新05/25/16 :密切关注快速进化提案https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md - 看起来有可能实施@atomic
行为自己。
答案 1 :(得分:10)
Swift没有围绕线程安全的语言结构。假设您将使用提供的库来执行您自己的线程安全管理。 在实现线程安全性方面有很多选项,包括pthread互斥锁,NSLock和dispatch_sync作为互斥机制。请参阅Mike Ash最近关于此主题的帖子:https://mikeash.com/pyblog/friday-qa-2015-02-06-locks-thread-safety-and-swift.html 因此,直接回答您的问题“我可以安全地并行读取和写入此变量吗?”是否。
答案 2 :(得分:7)
回答这个问题可能要早。目前swift缺少访问修饰符,因此没有明显的方法来添加管理属性getter / setter的并发性的代码。此外,Swift语言似乎还没有关于并发性的任何信息! (它也缺少KVO等......)
我认为这个问题的答案在未来的版本中会变得清晰。
答案 3 :(得分:4)
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
}
}
}
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()
}
}
}
}
Atomic().semaphoreSample()
//Atomic().dispatchQueueSync()
//Atomic().objcSync()
答案 4 :(得分:1)
这是我广泛使用的原子属性包装器。我将实际的锁定机制设置为协议,因此可以尝试使用不同的机制。我尝试了信号灯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()
}
}
答案 5 :(得分:1)
在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
}
}
使用方法:
class Shared {
@atomic var value: Int
...
}