万一你想知道,这是我目前正在努力的DI library.
我正在谈论的代码是the following:
final class Builder<I> { private let body: () -> I private var instance: I? private let instanceLocker = NSLock() private var isSet = false private let isSetdispatchQueue = dispatchQueue(label: "\(Builder.self)",attributes: .concurrent) init(body: @escaping () -> I) { self.body = body } private var syncIsSet: Bool { set { isSetdispatchQueue.async(flags: .barrier) { self.isSet = newValue } } get { var isSet = false isSetdispatchQueue.sync { isSet = self.isSet } return isSet } } var value: I { if syncIsSet { return instance! // should never fail } instanceLocker.lock() if syncIsSet { instanceLocker.unlock() return instance! // should never fail } let instance = body() self.instance = instance syncIsSet = true instanceLocker.unlock() return instance } }
逻辑是允许并发读取isSet,因此对实例的访问可以从不同的线程并行运行.为了避免竞争条件(这是我不能100%肯定的部分),我有两个障碍.设置为Set时设置一个,设置实例时设置一个.诀窍是仅在isSet设置为true后解锁后者,因此等待onLocker解锁的线程在isSet上被异步写入并发调度队列时第二次被锁定.
我认为我离这里的最终解决方案非常接近,但由于我不是分布式系统专家,所以我想确定一下.
此外,使用调度队列不是我的第一选择,因为它让我觉得阅读isSet不是超级高效,但同样,我不是专家.
所以我的两个问题是:
>这是100%线程安全的,如果没有,为什么?
>有效率吗?
在Swift中这样做的方式?
解决方法
final class Builder<I> { private let body: () -> I private var lock = os_unfair_lock() init(body: @escaping () -> I) { self.body = body } private var _value: I! var value: I { os_unfair_lock_lock(&lock) if _value == nil { _value = body() } os_unfair_lock_unlock(&lock) return _value } }
请注意,在syncIsSet上执行同步是正确的.如果你把它当作一个原语(在其他的双重检查同步中很常见),那么你将依赖于Swift不承诺的东西(编写Bools的原子和它实际上会检查布尔值)两次,因为没有波动).鉴于您正在进行同步,比较是在os_unfair_lock和调度到队列之间进行的.
这说,根据我的经验,这种懒惰在移动应用程序中几乎总是没有根据的.如果变量非常昂贵,但可能永远不会访问,它实际上只会节省您的时间.有时在大规模并行系统中,能够移动初始化是值得的,但是移动应用程序存在于相当有限数量的核心上,因此通常没有一些额外的核心可以将其分流.我通常不会追求这个,除非你已经发现当你的框架在实时系统中使用时这是一个重大问题.如果有,那么我建议在显示此问题的实际用法中对os_unfair_lock进行分析.我希望os_unfair_lock获胜.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。