class MyClass {
static var name: String = "Hello"
}
默认情况下,swift中的静态变量不是线程安全的。如果要使它们成为线程安全的,该如何实现?
答案 0 :(得分:3)
通过与静态变量进行同步,您可以像其他变量一样对线程进行静态更改。例如,您可以将您的公开属性设置为计算属性,该属性将同步对某些私有属性的访问。例如:
class MyClass {
private static let lock = NSLock()
private static var _name: String = "Hello"
static var name: String {
get { lock.withCriticalSection { _name } }
set { lock.withCriticalSection { _name = newValue } }
}
}
哪里
extension NSLocking {
func withCriticalSection<T>(block: () throws -> T) rethrows -> T {
lock()
defer { unlock() }
return try block()
}
}
或者您也可以使用GCD串行队列或读写器进行同步。这个想法是一样的。
tl; dr
值得注意的是,这种属性级同步实现了线程安全性的低级概念,但这通常是不够的。通常,线程安全性是在更高的抽象级别上实现的。
考虑:
let group = DispatchGroup()
DispatchQueue.global().async(group: group) {
for _ in 0 ..< 100_000 {
MyClass.name += "x"
}
}
DispatchQueue.global().async(group: group) {
for _ in 0 ..< 100_000 {
MyClass.name += "y"
}
}
group.notify(queue: .main) {
print(MyClass.name.count)
}
您会认为,因为我们有线程安全访问器,所以一切正常。但事实并非如此。这不会为name
添加200,000个字符。您必须执行以下操作:
class MyClass {
private static let lock = NSLock()
private static var _name: String = ""
static var name: String {
get { lock.withCriticalSection { _name } }
}
static func appendString(_ string: String) {
lock.withCriticalSection {
_name += string
}
}
}
然后执行以下操作:
let group = DispatchGroup()
DispatchQueue.global().async(group: group) {
for _ in 0 ..< 100_000 {
MyClass.appendString("x")
}
}
DispatchQueue.global().async(group: group) {
for _ in 0 ..< 100_000 {
MyClass.appendString("y")
}
}
group.notify(queue: .main) {
print(MyClass.name.count)
}
另一个经典示例是您有两个相互关联的属性,例如,firstName
和lastName
。您不仅可以使两个属性中的每一个都具有线程安全性,而且还需要使更新两个属性的所有任务都成为线程安全。
这些是愚蠢的示例,但说明有时需要更高级别的抽象。但是对于简单的应用程序,同步计算的属性的访问器方法可能就足够了。