swift - 如何借助信号量/锁解决数据竞争/读写问题?
问题描述
是否可以借助信号量或锁来解决读写问题?可以使解决方案具有串行写入和串行读取,但是否可以进行并发读取(有可能同时进行一次读取)?
这是我的简单实现,但读取不是并发的。
class ThreadSafeContainerSemaphore<T> {
private var value: T
private let semaphore = DispatchSemaphore(value: 1)
func get() -> T {
semaphore.wait()
defer { semaphore.signal() }
return value
}
func mutate(_ completion: (inout T) -> Void) {
semaphore.wait()
completion(&self.value)
semaphore.signal()
}
init(value: T) {
self.value = value
}
}
解决方案
您问:
是否可以借助信号量或锁来解决读写问题?
是的。您提供的方法应该可以做到这一点。
可以使解决方案具有串行写入和串行读取,但是否可以进行并发读取(有可能同时进行一次读取)?
那更复杂。信号量和锁适用于禁止任何并发访问(包括禁止并发读取)的简单同步。
允许并发读取的方法称为“读写器”模式。但是信号量/锁在不添加各种状态属性的情况下自然不会适用于读写器模式。我们通常使用并发 GCD 队列来完成它,并发执行读取,但使用屏障执行写入(以防止任何并发操作):
class ThreadSafeContainerGCD<Value> {
private var value: Value
private let queue = DispatchQueue(label: ..., attributes: .concurrent)
func get() -> Value {
queue.sync { value }
}
func mutate(_ block: @escaping (inout Value) -> Void) {
queue.async(flags: .barrier) { block(&self.value) }
}
init(value: Value) {
self.value = value
}
}
几点观察:
信号量相对低效。在我的基准测试中,简单
NSLock
的锁要快得多,不公平的锁更是如此。GCD 读写器模式虽然比信号量模式更有效,但仍然不如简单的锁方法快(即使后者不支持并发读取)。GCD 开销超过了并发读取和异步写入所带来的好处。
但是对您的用例中的各种模式进行基准测试,看看哪种模式最适合您。请参阅https://stackoverflow.com/a/58211849/1271826。
推荐阅读
- laravel - 部署到托管 Laravel 8 后找不到模型
- android - Android 中的 NestedScrollingChild、NestedScrollingChild2 和 NestedScrollingChild3 有什么区别?
- jquery - 试图在多个同位素实例上集成分页
- python - Windows Run 找不到 Al Sweigart 的“Automate the Boring Stuff with Python”第 6 章中“mclip”的批处理文件
- android - 当文件名中不存在扩展名时如何获取文件扩展名?
- tensorflow - 寻找 TensorFlow 等效的 Pytorch GRU 功能
- excel - VBA 运行时错误 91,对象变量或未设置块变量
- android - 如何使 PANIC BUTTON 在蓝牙上工作
- google-apps-script - 如果单元格值不是 Google App Scripts 中的电子邮件地址,如何添加条件
- linux - 在 emacs 中未检测到“Control+t”