Swift变量是原子的吗?

las*_*sej 98 objective-c swift

在Objective-C中,您可以区分原子和非原子属性:

@property (nonatomic, strong) NSObject *nonatomicObject;
@property (atomic, strong) NSObject *atomicObject;
Run Code Online (Sandbox Code Playgroud)

根据我的理解,您可以安全地读取和写入从多个线程定义为原子的属性,而同时从多个线程编写和访问非原子属性或ivars可能导致未定义的行为,包括错误的访问错误.

所以如果你在Swift中有这样的变量:

var object: NSObject
Run Code Online (Sandbox Code Playgroud)

我可以安全地并行读取和写入此变量吗?(不考虑这样做的实际意义).

Sas*_*ats 51

现在假设没有低级文档可用,但您可以从汇编中学习.Hopper Disassembler是一个很棒的工具.

@interface ObjectiveCar : NSObject
@property (nonatomic, strong) id engine;
@property (atomic, strong) id driver;
@end
Run Code Online (Sandbox Code Playgroud)

分别用于objc_storeStrongobjc_setProperty_atomic非原子和原子,其中

class SwiftCar {
    var engine : AnyObject?    
    init() {
    }
}
Run Code Online (Sandbox Code Playgroud)

使用swift_retainfrom libswift_stdlib_core和,显然,没有内置的线程安全.

我们可以推测@lazy以后可能会引入其他关键字(类似).

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 {

}
Run Code Online (Sandbox Code Playgroud)

更新05/25/16:密切关注快速进化建议https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md - 它看起来像是将有可能@atomic自己实施行为.


Goo*_*oug 11

Swift没有围绕线程安全的语言结构.假设您将使用提供的库来执行您自己的线程安全管理.在实现线程安全性方面有很多选项,包括pthread互斥锁,NSLock和dispatch_sync作为互斥机制.请参阅Mike Ash最近关于这个主题的帖子:https://mikeash.com/pyblog/friday-qa-2015-02-06-locks-thread-safety-and-swift.html 所以直接回答你的问题"可以我安全地并行读写这个变量?" 没有.


Col*_*inE 7

可能要早点回答这个问题.目前swift缺少访问修饰符,因此没有明显的方法来添加管理属性getter/setter的并发性的代码.此外,Swift语言似乎没有关于并发性的任何信息!(它也缺少KVO等......)

我认为这个问题的答案将在未来的版本中变得清晰.


Vas*_*huk 6

细节

  • Xcode 9.1,斯威夫特 4
  • Xcode 10.2.1 (10E1001),Swift 5

链接

实现类型

大意

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

原子访问示例

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()
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

Atomic().semaphoreSample()
//Atomic().dispatchQueueSync()
//Atomic().objcSync()
Run Code Online (Sandbox Code Playgroud)

结果

在此处输入图片说明


iUr*_*rii 6

从 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
    }
}
Run Code Online (Sandbox Code Playgroud)

如何使用:

class Shared {
    @atomic var value: Int
...
}
Run Code Online (Sandbox Code Playgroud)