如何借助信号量/锁解决数据竞争/读写问题?
Posted
技术标签:
【中文标题】如何借助信号量/锁解决数据竞争/读写问题?【英文标题】:How to solve data race/ read and write problem with a help of semaphore/lock? 【发布时间】:2021-10-08 09:29:54 【问题描述】:是否可以借助信号量或锁来解决读写问题? 可以使解决方案具有串行写入和串行读取,但是否可以进行并发读取(提供一次并发读取的可能性)?
这是我的简单实现,但读取不是并发的。
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
【问题讨论】:
【参考方案1】:-
是的,您可以用信号量适当地解决您的问题。它也用于访问共享资源。
并行读取没有问题,但是如果你想实现即使是单次写入,那么你需要小心并妥善处理。 (即使你有并行单写+单读)
【讨论】:
我在原始问题中添加了一个片段。但我不知道如何使读取并发。也许您可以提出一些解决方案? @Sasha 我稍微修改了我的答案,这样会更清楚。无论如何,如果没有并行写入,您不必为读取而烦恼。这是video link about accessing shared resource with semaphore,希望对您有所帮助:)【参考方案2】:你问:
是否可以借助信号量或锁来解决读写问题?
是的。您提供的方法应该可以做到这一点。
可以使解决方案具有串行写入和串行读取,但是否可以进行并发读取(可以同时进行并发读取)?
那就更复杂了。信号量和锁适用于禁止任何并发访问(包括禁止并发读取)的简单同步。
允许并发读取的方法称为“读写器”模式。但是信号量/锁在不添加各种状态属性的情况下自然不会适用于读写器模式。我们一般使用并发 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://***.com/a/58211849/1271826。
【讨论】:
以上是关于如何借助信号量/锁解决数据竞争/读写问题?的主要内容,如果未能解决你的问题,请参考以下文章