如何线程安全存档一组自定义对象?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何线程安全存档一组自定义对象?相关的知识,希望对你有一定的参考价值。
我有类型Set<CostumObject>
的实例,我想用NSKeyedArchiver
存档。
假设customObject1: CostumObject
和customObject2: CostumObject
在某处实例化。
如果我使用以下语句:
let setOfCostomObjects: Set<CostumObject> = [customObject1, customObject2]
let data = NSKeyedArchiver.archivedData(withRootObject: setOfCostomObjects)
NSKeyedArchiver
按顺序归档两个自定义对象,其属性以递归方式存档。
这不是线程安全的,因为另一个线程可以在归档期间改变自定义对象及其属性。
我认为我可以线程安全地归档自定义对象的每个属性,因此允许并发获取只允许一个集合,通过使用带有屏障的并发队列,如:
private let concurrentPropertyAccessQueue = DispatchQueue(label: "concurrentPropertyAccessQueue", attributes: .concurrent)
…
private var safeProperty = CostumProperty.init()
public private(set) var property: CostumProperty {
get {
var result = CostumProperty.init()
concurrentPropertyAccessQueue.sync { result = safeProperty } // sync, because result is returned
return result
} // get
set { concurrentPropertyAccessQueue.async(flags: .barrier) { safeProperty = newValue } // executes locked after all gets
} // set
}
…
public func threadSafeArchiveOfProperty() -> Data {
var data = Data.init()
concurrentPropertyAccessQueue.sync { // sync, because result is returned
data = NSKeyedArchiver.archivedData(withRootObject: self.safeProperty)
}
return data
}
我想我也可以用类似的方式线程安全地存档整个自定义对象:
private let concurrentObjectAccessQueue = DispatchQueue(label: "concurrentObjectAccessQueue", attributes: .concurrent)
…
public func encode(with aCoder: NSCoder) {
concurrentObjectAccessQueue.async(execute: {
aCoder.encode(self.property forKey: "property")
…
})
}
问题仍然是,如何线程安全地存档自定义对象集。 这将要求在归档期间锁定对该组元素的写访问。
一种方法是定义全局并发队列:
public let globalConcurrentAccessQueue = DispatchQueue(label: "globalConcurrentAccessQueue", attributes: .concurrent)
要在归档期间锁定集合及其所有元素,可以编写一个Set
类型的扩展,它定义了如上所述的func threadSafeArchiveOfSet()
。
然后,此函数将覆盖Set的encode(with aCoder: NSCoder)
,以便锁定globalConcurrentAccessQueue
。
这是正确的方法吗? 我认为这是一个应该有标准解决方案的标准问题。
通常,属性级同步是不恰当的。它提供对各个属性的线程安全访问,但是它不能确保对不同属性之间可能存在相互依赖性的更广泛对象的线程安全访问。原型示例是具有名字和姓氏属性的Person
对象。分别对名字和姓氏进行同步更改仍然可以在内部不一致状态下捕获对象。您经常需要在更高级别同步对象,如果这样做,它将呈现属性级别同步redundent。
一些不相关的观察:
encode
方法必须同步执行其任务,而不是异步执行。调用者假定编码在返回时完成。我可以猜到为什么你可能让它异步(例如它毕竟没有明确地返回任何东西),但问题不在于是否返回任何东西,而是更广泛地说是否有任何副作用。同步对象。在这种情况下(你正在更新NSCoder
对象),所以你必须在sync
中使用encode
。- 有几次你使用初始化变量的模式,调用
sync
来修改该局部变量,然后返回该值。例如。func threadSafeArchiveOfProperty() -> Data { var data = Data.init() concurrentPropertyAccessQueue.sync { // sync, because result is returned data = NSKeyedArchiver.archivedData(withRootObject: self.safeProperty) } return data }
但sync
提供了一种简化这种方法的好方法,即如果闭包返回一个值,sync
也将返回它。如果闭包只有一行,你甚至不需要在闭包中显式的return
:func threadSafeArchiveOfProperty() -> Data { return concurrentPropertyAccessQueue.sync { // sync, because result is returned NSKeyedArchiver.archivedData(withRootObject: self.safeProperty) } }
Basem Emara将here描述为线程安全数组的解决方案,也可以应用于集合:
他宣布SynchronizedArray
模仿一个常规阵列。它包含一个私有并发队列和数组,并暴露了几个数组的属性和方法。
不可变访问是同步和同时完成的,而可变访问是与屏障异步完成的,即在队列中的所有其他块终止之后。
以上是关于如何线程安全存档一组自定义对象?的主要内容,如果未能解决你的问题,请参考以下文章