首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >NSLock在属性设置器中的应用

NSLock在属性设置器中的应用
EN

Stack Overflow用户
提问于 2020-04-30 16:26:20
回答 2查看 939关注 0票数 0

比方说,有一个变量是我想让线程安全的。最常见的方法之一是:

代码语言:javascript
复制
var value: A {
    get { return queue.sync { self._value } }
    set { queue.sync { self._value = newValue } }
}

但是,如果我们如下面的示例所示更改该值,则为此属性不是完全线程安全的。

代码语言:javascript
复制
Class.value += 1

因此,我的问题是:在相同的原则上使用NSLock,也不是完全线程安全?

代码语言:javascript
复制
var value: A {
    get { 
       lock.lock()
       defer { lock.unlock() }
       return self._value
    }
    set { 
       lock.lock()
       defer { lock.unlock() }
       self._value = newValue
    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-04-30 16:34:25

很有趣,我是第一次学到这个。

第一段代码中的问题是:

object.value += 1

具有与

object.value = object.value + 1

我们可以进一步扩展到:

代码语言:javascript
复制
let originalValue = queue.sync { object._value }
let newValue = origiinalValue + 1
queue.sync { self._value = newValue }

扩展它可以清楚地表明getter和setter的同步很好,但是它们不是作为一个整体同步的。上面代码中间的上下文开关可能导致_value被另一个线程更改,而不需要newValue来反映更改。

使用锁也会有同样的问题。它将扩大到:

代码语言:javascript
复制
lock.lock()
let originalValue = object._value
lock.unlock()

let newValue = originalValue + 1

lock.lock()
object._value = newValue
lock.unlock()

通过使用一些日志语句对代码进行检测,您可以看到这一点,这些语句表明,锁并没有完全覆盖这种变异:

代码语言:javascript
复制
class C {
    var lock = NSLock()

    var _value: Int
    var value: Int {
        get {
            print("value.get start")
            print("lock.lock()")
            lock.lock()
            defer {
                print("lock.unlock()")
                lock.unlock()
                print("value.get end")
            }
            print("getting self._value")
            return self._value
        }
        set { 
            print("\n\n\nvalue.set start")
            lock.lock()
            print("lock.lock()")
            defer {
                print("lock.unlock()")
                lock.unlock()
                print("value.set end")
            }
            print("setting self._value")
            self._value = newValue
        }
    }

    init(_ value: Int) { self._value = value }
}

let object = C(0)
object.value += 1
票数 1
EN

Stack Overflow用户

发布于 2021-06-29 21:44:03

在回答您的问题时,锁方法遇到了与GCD方法完全相同的问题。原子访问器方法仅仅不足以确保更广泛的线程安全。

问题是,正如其他地方所讨论的,无害的+=操作符正在通过getter检索值,递增该值,并通过setter存储新值。为了实现线程安全,需要将整个进程封装在一个单一的同步机制中。如果您想要一个原子增量操作,您可以编写一个方法来完成这个操作。

因此,以您的NSLock为例,我可能将同步逻辑移动到它自己的方法中,例如:

代码语言:javascript
复制
class Foo<T> {
    private let lock = NSLock()
    private var _value: T
    init(value: T) {
        _value = value
    }

    var value: T {
        get { lock.synchronized { _value } }
        set { lock.synchronized { _value = newValue } }
    }
}

extension NSLocking {
    func synchronized<T>(block: () throws -> T) rethrows -> T {
        lock()
        defer { unlock() }
        return try block()
    }
}

但是,如果您希望有一个以线程安全方式增加值的操作,您可以编写一个方法来实现这一点,例如:

代码语言:javascript
复制
extension Foo where T: Numeric {
    func increment(by increment: T) {
        lock.synchronized {
            _value += increment
        }
    }
}

然后,而不是这种非线程安全的尝试:

代码语言:javascript
复制
foo.value += 1

相反,您将使用以下线程安全的呈现:

代码语言:javascript
复制
foo.increment(by: 1)

这种模式将增量过程包装在自己的方法中以同步整个操作,无论您使用哪种同步机制(例如锁、GCD串行队列、读取器模式、os_unfair_lock等),这种模式都是适用的。

就其价值而言,SWIFT5.5 actor模式(在硒-0306中概述)将此模式形式化。考虑:

代码语言:javascript
复制
actor Bar<T> {
    var value: T

    init(value: T) {
        self.value = value
    }
}

extension Bar where T: Numeric {
    func increment(by increment: T) {
        value += increment
    }
}

在这里,increment方法自动是一个“独立于参与者”的方法(也就是说,它将被同步),但是actor将控制它的属性与setter的交互,也就是说,如果您试图从这个类之外设置value,您将收到一个错误:

参与者-孤立的属性“值”只能从参与者内部发生变异。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61528184

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档