Являются ли переменные Свифта атомарными?
В Objective-C у вас есть различие между атомными и неатомическими свойствами:
@property (nonatomic, strong) NSObject *nonatomicObject;
@property (atomic, strong) NSObject *atomicObject;
Из моего понимания вы можете безопасно читать и записывать свойства, определенные как атомарные из нескольких потоков, при одновременном написании и доступе к неатомическим свойствам или ivars из нескольких потоков в результате могут возникать поведение undefined, включая ошибки с плохим доступом.
Итак, если у вас есть переменная типа Swift:
var object: NSObject
Можно ли безопасно читать и записывать эту переменную параллельно? (Без учета фактического значения этого).
Ответы
Ответ 1
Очень рано предполагать, что документация низкого уровня недоступна, но вы можете учиться на сборке. 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
) могут быть введены позже.
Обновление 07/20/15: в соответствии с этим blogpost на singleletons быстрая среда может сделать определенные потоки безопасными для вас, то есть:
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
, реализованное вами самостоятельно.
Ответ 2
У Swift нет языковых конструкций вокруг безопасности потоков. Предполагается, что вы будете использовать предоставленные библиотеки для управления своими потоками.
Существует большое количество возможностей для обеспечения безопасности потоков, включая мьютексы pthread, NSLock и dispatch_sync в качестве механизма взаимного доступа. См. Недавнее сообщение Майка Эша на тему: https://mikeash.com/pyblog/friday-qa-2015-02-06-locks-thread-safety-and-swift.html
Поэтому прямой ответ на ваш вопрос "Могу ли я читать и записывать эту переменную параллельно безопасно?" Нет.
Ответ 3
Вероятно, рано ответить на этот вопрос. В настоящее время быстро отсутствуют модификаторы доступа, поэтому нет очевидного способа добавить код, который управляет concurrency вокруг свойства getter/setter. Более того, у Swift Language пока нет информации о concurrency! (Он также не имеет KVO и т.д.)
Я думаю, что ответ на этот вопрос станет ясен в будущих выпусках.
Ответ 4
подробности
- Xcode 9.1, Swift 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
}
}
}
Образец атомарного доступа
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()
Результат
![enter image description here]()