如何以线程安全的方式捕获属性 [英] how do I capture a property in thread-safe way

查看:24
本文介绍了如何以线程安全的方式捕获属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的班级 A 具有班级 B 的属性,可以重置:

My class A has a property of class B which can be reset:

class A1 {
    private var b = B(0)

    func changeB(i : Int) {
        b = B(i)
    }

    func testB(k : Int) -> Bool {
        return b.test(k)
    }
}

class B {
    private let b : Int;

    init(_ i : Int) {
        b = i
    }

    func test(_ k : Int) -> Bool {
        return b == k
    }
}

到目前为止一切顺利.如果我想在多线程场景中使用 A 类,我必须添加一些同步机制,因为 Swift 中的属性 本身不是原子的:

So far so good. If I want to use class A in multithreading scenario, I must add some synchronization mechanism, because properties in Swift are not atomic by themselves:

class A2 {
    private var b = B(0)
    private let lock = NSLock()

    func changeB(i : Int) {
        lock.lock()
        defer { lock.unlock() }
        b = B(i)
    }
    
    func testB(k : Int) -> Bool {
        lock.lock()
        defer { lock.unlock() }
        return b.test(k)
    }

}

但现在我想介绍一个闭包:

But now I want to introduce a closure:

class A3 {
    func listenToB() {
        NotificationCenter.default.addObserver(forName: Notification.Name("B"), object: nil, queue: nil) { 
        [b] (notification) in
            let k = notification.userInfo!["k"] as! Int
            print(b.test(k))
        }
    }
}

我是否正确理解这不是线程安全的?如果我也捕获 lock,这会得到解决吗,如下所示?

Do I understand correctly that this is not thread-safe? Will this get fixed if I capture lock as well, as below?

class A4 {
    func listenToB() {
        NotificationCenter.default.addObserver(forName: Notification.Name("B"), object: nil, queue: nil) { 
        [lock, b] (notification) in
            let k = notification.userInfo!["k"] as! Int
            lock.lock()
            defer { lock.unlock() }
            print(b.test(k))
        }
    }
}

推荐答案

是的,使用捕获的 lock 确保观察者的闭包与使用相同锁的其他任务同步.您可以使用这种捕获模式,因为 lock 恰好是一个常量.

Yes, using the captured lock ensures that the observer’s closure is synchronized with other tasks using the same lock. You can use this capturing pattern because lock happens to be a constant.

这就引出了一个更基本的问题,即捕获不是常量的 b 引用.这意味着如果您在某个中间时间点调用 changeB,您的通知块仍将引用原始捕获的 B,而不是新的.

That raises the more fundamental problem, namely the capturing of the b reference, which is not constant. That means that if you call changeB at some intervening point in time, your notification block will still reference the original captured B, not the new one.

所以,如果你想让它引用当前的 B,你真的想回到 weak self 模式:

So, you really want to fall back to the weak self pattern if you want this to reference the current B:

class A {
    func listenToB() {
        NotificationCenter.default.addObserver(forName: Notification.Name("B"), object: nil, queue: nil) { [weak self] notification in
            guard let self = self else { return }

            let k = notification.userInfo!["k"] as! Int
            self.lock.lock()
            defer { self.lock.unlock() }
            print(self.b.test(k))
        }
    }
}

这篇关于如何以线程安全的方式捕获属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆