nik*_*dia 5 thread-safety ios swift
class MyClass {
static var name: String = "Hello"
}
Run Code Online (Sandbox Code Playgroud)
默认情况下,swift 中的静态变量不是线程安全的。如果我想让它们线程安全,我该如何实现?
static变量的初始化是线程安全的。但是,如果对象本身不是线程安全的,则必须从多个线程同步您与它的交互(就像您必须与任何非线程安全的对象一样,无论是否static如此)。
至少,您可以使公开的属性成为同步访问某些私有属性的计算属性。例如:
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 } }
}
}
Run Code Online (Sandbox Code Playgroud)
在哪里
extension NSLocking {
func withCriticalSection<T>(block: () throws -> T) rethrows -> T {
lock()
defer { unlock() }
return try block()
}
}
Run Code Online (Sandbox Code Playgroud)
或者您也可以使用 GCD 串行队列、读写器或各种其他机制进行同步。不过,基本的想法是一样的。
话虽如此,值得注意的是,这种属性访问器同步对于可变类型是不够的。需要更高级别的同步。
考虑:
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)
}
Run Code Online (Sandbox Code Playgroud)
你会认为因为我们有线程安全的访问器,所以一切都很好。但事实并非如此。这不会将 200,000 个字符添加到name. 您必须执行以下操作:
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后以下工作:
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)
}
Run Code Online (Sandbox Code Playgroud)
另一个经典示例是您有两个相互关联的属性,例如,maybefirstName和lastName。您不能只使两个属性中的每一个都成为线程安全的,而是需要使更新这两个属性的单个任务成为线程安全的。
这些都是愚蠢的例子,但说明有时需要更高级别的抽象。但是对于简单的应用程序,同步计算属性的访问器方法可能就足够了。
作为澄清的一点,虽然像全局变量一样,静态变量是惰性实例化的,但带有lazy限定符的标准存储属性不是线程安全的。正如The Swift Programming Language: Properties警告我们:
如果一个用
lazy修饰符标记的属性被多个线程同时访问并且该属性尚未初始化,则不能保证该属性只会被初始化一次。
| 归档时间: |
|
| 查看次数: |
2051 次 |
| 最近记录: |