此OSAtomic API 已弃用。文档没有提到它,你也没有看到来自 Swift 的警告,但是从 Objective-C 中使用你会收到弃用警告:
'OSAtomicCompareAndSwap64Barrier' 已弃用:首先在 iOS 10 中弃用 - 改用 atomic_compare_exchange_strong()
(如果在 macOS 上工作,它会警告您它已在 macOS 10.12 中弃用。)
见How do I atomically increment a variable in Swift?
你问:
OSAtomic 界面现已弃用,但我认为任何替代品都或多或少具有相同的功能和相同的幕后行为。所以 32 位和 64 位值是否可以安全读取的问题仍然存在。
建议的替换是stdatomic.h。它有一个atomic_load 方法,我会使用它而不是直接访问。
就个人而言,我建议你不要使用OSAtomic。在 Objective-C 中,您可以考虑使用 stdatomic.h,但在 Swift 中,我建议使用标准的通用同步机制之一,例如 GCD 串行队列、GCD 读写器模式或基于 NSLock 的方法。传统观点认为 GCD 比锁更快,但我最近的所有基准测试似乎表明现在情况正好相反。
所以我可能会建议使用锁:
struct Synchronized<Value> {
private var _value: Value
private var lock = NSLock()
init(_ value: Value) {
self._value = value
}
var value: Value {
get { lock.synchronized { _value } }
set { lock.synchronized { _value = newValue } }
}
mutating func synchronized<T>(block: (inout Value) throws -> T) rethrows -> T {
return try lock.synchronized {
try block(&_value)
}
}
}
通过这个小扩展(受 Apple 的 withCriticalSection 方法启发)提供更简单的 NSLock 交互:
extension NSLocking {
func synchronized<T>(block: () throws -> T) rethrows -> T {
lock()
defer { unlock() }
return try block()
}
}
然后,我可以声明一个同步整数:
var foo = Synchronized<Int>(0)
现在我可以像这样从多个线程中增加一百万次:
DispatchQueue.concurrentPerform(iterations: 1_000_000) { _ in
foo.synchronized { value in
value += 1
}
}
print(foo.value) // 1,000,000
注意,虽然我为 value 提供了同步访问器方法,但这仅适用于简单的加载和存储。我在这里没有使用它,因为我们希望整个加载、增量和存储作为单个任务同步。所以我使用synchronized 方法。考虑以下几点:
DispatchQueue.concurrentPerform(iterations: 1_000_000) { _ in
foo.value += 1
}
print(foo.value) // not 1,000,000 !!!
这看起来很合理,因为它使用了同步的value 访问器。但它只是不起作用,因为同步逻辑处于错误的级别。我们确实需要将所有三个步骤同步在一起,而不是单独同步该值的加载、增量和存储。因此,我们将整个 value += 1 包装在 synchronized 闭包中,如前面的示例所示,并实现所需的行为。
顺便说一句,请参阅Use queue and semaphore for concurrency and property wrapper? 了解这种同步机制的其他一些实现,包括 GCD 串行队列、GCD 读写器、信号量等,以及一个不仅对这些进行基准测试的单元测试,而且还说明简单的原子访问器方法不是线程安全的。
如果你真的想使用stdatomic.h,你可以在Objective-C中实现它:
// Atomic.h
@import Foundation;
NS_ASSUME_NONNULL_BEGIN
@interface AtomicInt: NSObject
@property (nonatomic) int value;
- (void)add:(int)value;
@end
NS_ASSUME_NONNULL_END
和
// AtomicInt.m
#import "AtomicInt.h"
#import <stdatomic.h>
@interface AtomicInt()
{
atomic_int _value;
}
@end
@implementation AtomicInt
// getter
- (int)value {
return atomic_load(&_value);
}
// setter
- (void)setValue:(int)value {
atomic_store(&_value, value);
}
// add methods for whatever atomic operations you need
- (void)add:(int)value {
atomic_fetch_add(&_value, value);
}
@end
然后,在 Swift 中,您可以执行以下操作:
let object = AtomicInt()
object.value = 0
DispatchQueue.concurrentPerform(iterations: 1_000_000) { _ in
object.add(1)
}
print(object.value) // 1,000,000
很明显,您可以将所需的任何原子操作添加到您的 Objective-C 代码中(我只实现了atomic_fetch_add,但希望它能说明这个想法)。
就我个人而言,我会坚持使用更传统的 Swift 模式,但如果您真的想使用建议的替代 OSAtomic,那么实现可能看起来像这样。