我的应用程序使用类
A
及其子类 SubA_x
。
A
有一个静态属性 serialNumber
,仅在子类初始化时由类 A
修改。
它设置一个属性let name
,以便每个子类都有一个唯一的名称。
这是代码:
class A {
static var serialNumber = 0
let name: String
init( /* some parameters */ ) {
A.serialNumber += 1
self.name = "\(A.serialNumber)"
}
}
final class SubA_1: A {
init( /* some parameters */ ) {
super.init( /* some parameters */ )
}
}
使用 Swift 6 严格的并发检查,初始化
serialNumber
的行给出错误
静态属性“serialNumber”不是并发安全的,因为它是 非隔离的全局共享可变状态
我知道
A
的每个子类都可以从任何线程修改 var serialNumber
,因此数据竞争是可能的。
但是如何以并发安全的方式实现此功能?
我试图通过演员提供
serialNumber
:
actor SerialNumberManager {
private var serialNumber = 0
func getNextSerialNumber() -> Int {
serialNumber += 1
return serialNumber
}
}
但是我无法在
getNextSerialNumber()
中调用 init
,除非 init
是异步的。
但是我只能在异步上下文中初始化子类,等等。
我可能可以提供自己的基于 GCD 的同步,但应该有一种方法可以在 Swift 并发中做到这一点。
如果您想要线程安全的共享状态,那么
actor
是一种合乎逻辑的方法。不过,如果您想要一个可以从同步上下文调用的管理器,您可以编写自己的管理器,实现您自己的手动同步(使用 GCD 串行队列,或者如下所示,使用锁):
class SerialNumberManager: @unchecked Sendable {
static let shared = SerialNumberManager()
private let lock = NSLock()
private var serialNumber = 0
private init() { }
func nextSerialNumber() -> Int {
lock.withLock {
serialNumber += 1
return serialNumber
}
}
}
注意,只有当我们实现了手动同步时,我们才会使用
@unchecked Sendable
,就像上面一样。
而且,你可以像这样使用它:
class A {
let serialNumber = SerialNumberManager.shared.nextSerialNumber()
let name: String
init( /* some parameters */ ) {
self.name = "\(serialNumber)"
}
}
OSAllocatedUnfairLock
,它已经是 Sendable
:
import os.lock
class A {
private static let serialNumber = OSAllocatedUnfairLock(initialState: 0)
let name: String
init( /* some parameters */ ) {
let value = Self.serialNumber.withLock { value in
value += 1
return value
}
self.name = "\(value)"
}
}