Как мы можем сделать «статические» переменные Thread-Safe в swift? - PullRequest
2 голосов
/ 21 сентября 2019
class MyClass {
     static var name: String = "Hello"
}

Статические переменные в swift не являются потокобезопасными по умолчанию.Если я хочу сделать их поточно-ориентированными, как мне этого добиться?

1 Ответ

2 голосов
/ 21 сентября 2019

Вы делаете мутацию статики потокобезопасной, как и любую другую переменную, синхронизируя свое взаимодействие с ней.Например, вы можете сделать ваше открытое свойство вычисляемым свойством, которое синхронизирует доступ к некоторому частному свойству.Например:

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 или устройство чтения-записи для синхронизации.Идея была бы такой же, хотя:это часто неадекватно.Как правило, потокобезопасность достигается на более высоком уровне абстракции.

Учтите:

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)
}

Вы могли бы подумать, что, поскольку у нас есть поточно-ориентированные средства доступа, все в порядке.Но это не так.Это не добавит 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
        }
    }
}

И тогда сработает следующее:

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.Вы не можете просто сделать каждое из двух свойств поточно-ориентированным, но вам нужно выполнить одну задачу по обновлению обоих свойств как поточно-безопасных.

Это глупые примеры, но они демонстрируют, что иногда более высокий уровень абстракциинеобходим.Но для простых приложений может оказаться достаточной синхронизация методов доступа к вычисляемым свойствам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...