万一你想知道,这是我目前正在努力的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获胜.
总结以上是内存溢出为你收集整理的双重检查锁优化,以在Swift中实现线程安全的延迟加载全部内容,希望文章能够帮你解决双重检查锁优化,以在Swift中实现线程安全的延迟加载所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)