数据的读写一般容易引发几个问题:
- 在主线程中大量数据读写容易阻塞主线程,造成UI卡住。
- 读操作和写操作同时执行,或多个写操作同时执行,容易引发并发问题。
读写锁的说明参考:https://www.jianshu.com/p/a3a9b9460ab8
因此设计一个高效的线程安全存取器很有必要。
class GCDSafeAccessor: NSObject {
let qos: DispatchQoS
lazy var queue = DispatchQueue(label: "gcd_safe_accessor", qos: qos, attributes: .concurrent)
init(qos: DispatchQoS = .default) {
self.qos = qos
super.init()
}
// 小数据写入可以在主线程同步执行
func safeReadSync<T>(_ callback: @escaping () -> T?) -> T? {
var data: T?
queue.sync {
data = callback()
}
return data
}
// 大数据写入需要在子线程异步进行,避免主线程卡住
func safeReadAsync(_ callback: @escaping () -> Void) {
queue.async(execute: callback)
}
func safeWriteAsync(_ callback: @escaping () -> Void) {
queue.async(flags: .barrier, execute: callback)
}
}
使用方式:
var dict: [String: String] = ["key": "value"]
// 当前线程同步读取数据方式1
_ = accessor.safeReadSync {
let value = self.dict["key"]
}
// 当前线程同步读取数据方式2
let value: String? = accessor.safeReadSync {
let value = self.dict["key"]
return value
}
// 子线程异步读取数据
accessor.safeReadAsync {
let value = self.dict["key"]
}
// 子线程异步写数据
accessor.safeWriteAsync {
self.dict["key"] = "value"
}
GCDSafeAccessor 提供同步和异步两种方式读取数据,可针对大数据或者同时大量读操作选择异步方式。同步读取数据也提供两种回调方式,方式1 读取数据在闭包内返回,方式 2读取的数据作为接口返回值返回,可根据代码结构选择不同方式。
线程安全方面,如果对读和写同时加上相同锁,则效率不高,因为多个读操作不需要加锁。因此,异步读操作使用并发队列让多个读操作可以并发执行;写操作则使用barrier来实现与读操作的互斥,并且能让多个写操作同步进行。
网友评论