一尘不染

objc_sync_enter / objc_sync_exit无法与DISPATCH_QUEUE_PRIORITY_LOW一起使用

swift

我需要为我的应用程序读/写锁。我已阅读
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()

谢谢。


阅读 274

收藏
2020-07-07

共1个答案

一尘不染

objc_sync_enter是一个非常低级的原语,不能直接使用。这是@synchronizedObjC中旧系统的实现细节。即使那是非常过时的,通常也应该避免。

使用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”中的“屏障”意味着它是当时唯一允许在队列上运行的事物,因此所有先前的读取将已完成,并且所有将来的读取将被阻止,直到完成为止。这种方案意味着您可以拥有任意数量的并发读取器,而不会饿死写入器(因为将始终为写入器提供服务),并且写入从不阻塞。仅当存在实际争用时,读取才被阻止。在正常情况下,这是非常快的。

2020-07-07