我们如何在 swift 中使“静态”变量成为线程安全的?

问题描述 投票:0回答:1
class MyClass {
     static var name: String = "Hello"
}

swift 中的静态变量默认不是线程安全的。如果我想让它们线程安全,我该如何实现?

ios swift thread-safety
1个回答
16
投票

static
变量的初始化是线程安全的。但是,如果对象本身不是线程安全的,则必须从多个线程同步与它的交互(就像与任何非线程安全对象一样,无论是否
static
)。

至少,您可以使公开的属性成为计算属性,手动同步对某些私有属性的访问。例如,有锁(和

NSLocking
withLock
):

class MyClass {
    private static let lock = NSLock()
    private static var _name: String = "Hello"

    static var name: String {
        get { lock.withLock { _name } }
        set { lock.withLock { _name = newValue } }
    }
}

或者您也可以使用 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)
}

您可能会认为因为我们有线程安全的访问器,所以一切都好。但事实并非如此。这不会向

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
。您不能只使两个属性中的每一个都成为线程安全的,而是需要使更新这两个属性的单个任务成为线程安全的。

这些都是愚蠢的例子,但说明有时需要更高层次的抽象。但对于简单的应用程序,同步计算属性的访问器方法可能就足够了。


需要澄清的是,虽然静态变量(如全局变量)是延迟实例化的,但带有

lazy
限定符的标准存储属性是 not 线程安全的。正如Swift 编程语言:属性 警告我们:

如果用

lazy
修饰符标记的属性同时被多个线程访问并且该属性尚未初始化,则不能保证该属性只会被初始化一次。

© www.soinside.com 2019 - 2024. All rights reserved.