objc_sync_enter / objc_sync_exit无法与DISPATCH_QUEUE_PRIORITY_LOW一起使用
我需要为我的应用程序读/写锁。我已阅读
https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock
并写了我自己的类,因为在swift中没有读/写锁
class ReadWriteLock {
var logging = true
var b = 0
let r = "vdsbsdbs" // string1 for locking
let g = "VSDBVSDBSDBNSDN" // string2 for locking
func waitAndStartWriting() {
log("wait Writing")
objc_sync_enter(g)
log("enter writing")
}
func finishWriting() {
objc_sync_exit(g)
log("exit writing")
}
// ждет пока все чтение завершится чтобы начать чтение
// и захватить мютекс
func waitAndStartReading() {
log("wait reading")
objc_sync_enter(r)
log("enter reading")
b++
if b == 1 {
objc_sync_enter(g)
log("read lock writing")
}
print("b = \(b)")
objc_sync_exit(r)
}
func finishReading() {
objc_sync_enter(r)
b--
if b == 0 {
objc_sync_exit(g)
log("read unlock writing")
}
print("b = \(b)")
objc_sync_exit(r)
}
private func log(s: String) {
if logging {
print(s)
}
}
}
效果很好,直到我尝试从GCD线程使用它。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
当我尝试在某个时刻从不同的异步块使用此类时,它允许在写入被锁定时进行写入
这是示例日志:
wait reading
enter reading
read lock writing
b = 1
wait reading
enter reading
b = 2
wait reading
enter reading
b = 3
wait reading
enter reading
b = 4
wait reading
enter reading
b = 5
wait reading
enter reading
b = 6
wait reading
enter reading
b = 7
wait reading
enter reading
b = 8
wait reading
enter reading
b = 9
b = 8
b = 7
b = 6
b = 5
wait Writing
enter writing
exit writing
wait Writing
enter writing
因此,您可以看到g被锁定,但是objc_sync_enter(g)允许继续。为什么会发生这种情况?
顺便说一句,我检查了构造ReadWriteLock的次数,它是1。
为什么objc_sync_exit不能正常工作,并在未释放objc_sync_enter(g)时允许呢?
PS Readwirtelock定义为
class UserData {
static let lock = ReadWriteLock()
谢谢。
-
objc_sync_enter
是一个非常低级的原语,不能直接使用。这是@synchronized
ObjC中旧系统的实现细节。即使那是非常过时的,通常也应该避免。使用GCD队列可以最好地实现Cocoa中的同步访问。例如,这是一种实现读取器/写入器锁定(并行读取,互斥写入)的常用方法。
public class UserData { private let myPropertyQueue = dispatch_queue_create("com.example.mygreatapp.property", DISPATCH_QUEUE_CONCURRENT) private var _myProperty = "" // Backing storage public var myProperty: String { get { var result = "" dispatch_sync(myPropertyQueue) { result = self._myProperty } return result } set { dispatch_barrier_async(myPropertyQueue) { self._myProperty = newValue } } } }
您的所有并发属性可以共享一个队列,也可以为每个属性分配自己的队列。这取决于您期望多少争用(作家将锁定整个队列)。
“
dispatch_barrier_async”中的“屏障”意味着它是当时唯一允许在队列上运行的事物,因此所有先前的读取将已完成,并且所有将来的读取将被阻止,直到完成为止。这种方案意味着您可以拥有任意数量的并发读取器,而不会饿死写入器(因为将始终为写入器提供服务),并且写入从不阻塞。仅当存在实际争用时,读取才被阻止。在正常情况下,这是非常快的。