objc_sync_enter/objc_sync_exit无法使用DISPATCH_QUEUE_PRIORITY_LOW

Yeg*_*sky 27 multithreading ios 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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它工作得很好,直到我尝试从GCD线程使用它.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
Run Code Online (Sandbox Code Playgroud)

当我尝试在不同的异步块中使用此类时,它允许在写入被锁定时写入

这是样本日志:

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 
Run Code Online (Sandbox Code Playgroud)

所以,你可以看到g被锁定了,但是objc_sync_enter(g)允许继续.为什么会这样?

顺便说一句,我检查了ReadWriteLock构建的次数,它是1.

为什么objc_sync_exit不工作,并且在没有释放时允许objc_sync_enter(g)?

PS Readwirtelock定义为

class UserData {

    static let lock = ReadWriteLock()
Run Code Online (Sandbox Code Playgroud)

谢谢.

Rob*_*ier 39

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
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

所有并发属性都可以共享一个队列,或者您可以为每个属性提供自己的队列.这取决于您期望的争用(作者将锁定整个队列).

"dispatch_barrier_async"中的"屏障"意味着它是当时唯一允许在队列上运行的东西,因此所有先前的读取都将完成,并且将来所有读取都将被阻止,直到它完成.这种方案意味着您可以拥有任意数量的并发读取器,而不会使编写者匮乏(因为编写器将始终处于服务状态),并且写入永远不会阻塞.读取是阻塞的,并且仅在存在实际争用时.在正常的,无争议的情况下,这非常非常快.

  • 这是问题的唯一答案.`objc_sync_xxx`根本不适用于GCD线程.我没有找到一个很好的答案为什么,但`objc_sync_enter`不会阻止它应该.相反,切换到GCD,无论如何都提供功能更全面的系统.这里有很好的教程:https://www.raywenderlich.com/79149/grand-central-dispatch-tutorial-swift-part-1 (4认同)

Ale*_*zub 19

您是否100%确定您的块实际上是在不同的线程上执行的?

objc_sync_enter()/ objc_sync_exit()只保护您从不同线程访问的对象.它们在引擎盖下使用递归互斥锁,因此它们不会死锁或阻止您重复访问同一线程中的对象.

因此,如果您锁定一个异步块并在另一个中解锁,则在中间执行的第三个块可以访问受保护对象.


Sir*_*ton 10

这是很容易错过的那些非常微妙的细微差别之一。

Swift 中的锁

你必须非常小心你用作锁的东西。在 Swift 中,String是一个结构体,意味着它是传值的。

每当你打电话时objc_sync_enter(g),你不是在给它g,而是一份副本g。所以每个线程本质上都在创建自己的锁,这实际上就像根本没有锁一样。

使用 NSObject

不要使用Stringor Int,而是使用普通的NSObject

let lock = NSObject()

func waitAndStartWriting() {
    log("wait Writing")
    objc_sync_enter(lock)
    log("enter writing")
}


func finishWriting() {
    objc_sync_exit(lock)
    log("exit writing")
}
Run Code Online (Sandbox Code Playgroud)

那应该管它!