multithreading - 对类中变量的线程安全访问

标签 multithreading swift

在一个可能有多个线程运行的应用程序中,并且不确定这些方法是否会在多线程环境下被访问的可能性,但为了安全起见,我已经做了一个测试类来演示一种情况。

一个方法被编程为线程安全的(如果正确完成也请评论)但其余的不是。

在这种情况下,removeadd 中只有一行代码,是否有必要使它们成为线程安全的,或者是否要夸大其词。

import Foundation

class Some {}

class Test {
    var dict = [String: Some]()

    func has(key: String) -> Bool {
        var has = false
        dispatch_sync(dispatch_queue_create("has", nil), { [unowned self] in
            has = self.dict[key] != nil
        })
        return has
    }

    func remove(key: String) -> Some {
        var ob = dict[key]
        dict[key] = nil
        return ob
    }

    func add(key: String, ob: Some) {
        dict[key] = ob
    }
}

评论后编辑

class Some {}

class Test {
    var dict = [String: Some]()
    private let queue: dispatch_queue_t = dispatch_queue_create("has", DISPATCH_QUEUE_CONCURRENT)

    func has(key: String) -> Bool {
        var has = false
        dispatch_sync(queue) {
            has = self.dict[key] != nil
        }
        return has
    }

    func remove(key: String) -> Some? { //returns
        var removed: Some?
        dispatch_barrier_sync(queue) {
            removed = self.dict.removeValueForKey(key)
        }
        return removed
    }

    func add(key: String, ob: Some) { //not async
        dispatch_barrier_sync(queue) {
            self.dict[key] = ob
        }
    }
}

最佳答案

您检查 key 是否存在的方式不正确。您每次都在创建一个新队列,这意味着操作不会同步发生。

我的做法是这样的:

class Some {}

class Test {
    var dict = [String: Some]()
    private let queue: dispatch_queue_t = dispatch_queue_create("has", DISPATCH_QUEUE_CONCURRENT)

    func has(key: String) -> Bool {
        var has = false
        dispatch_sync(queue) { [weak self] in
            guard let strongSelf = self else { return }

            has = strongSelf.dict[key] != nil
        }

        return has
    }

    func remove(key: String) {
        dispatch_barrier_async(queue) { [weak self] in
            guard let strongSelf = self else { return }

            strongSelf.dict[key] = nil
        }
    }

    func add(key: String, ob: Some) {
        dispatch_barrier_async(queue) { [weak self] in
            guard let strongSelf = self else { return }

            strongSelf.dict[key] = ob
        }
    }
}

首先,我正在创建一个串行队列,它将用于访问作为对象属性的字典,而不是每次都创建一个新队列。该队列是私有(private)的,因为它仅在内部使用。

当我想从类中获取一个值时,我只是将一个 block 同步分派(dispatch)到队列并等待 block 完成,然后再返回队列是否存在。由于这不会改变字典,因此这种类型的多个 block 在并发队列上运行是安全的。

当我想在字典中添加或删除值时,我将 block 添加到队列中但带有障碍。这样做的目的是在队列运行时停止队列中的所有其他 block 。完成后,所有其他 block 可以同时运行。我正在使用异步调度,因为我不需要等待返回值。

假设您有多个线程试图查看键值是否存在或添加或删除值。如果您有很多读取操作,那么它们会同时发生,但是当运行其中一个将更改字典的 block 时,所有其他 block 将等待此更改完成,然后再次开始运行。

这样,在取值时既有并发运行的速度和便利性,也有字典变化时阻塞的线程安全性。

编辑添加

self 在 block 中被标记为 weak,因此它不会创建引用循环。正如评论中提到的@MartinR;有可能对象在 block 仍在队列中时被释放,如果发生这种情况,则 self 未定义,并且您可能会在尝试访问字典时遇到运行时错误,因为它也可能被释放。

通过在 block 内声明self设置为weak,如果对象存在,则self不会为nil,并且可以有条件地解包成strongSelf 指向 self 并且还创建了一个强引用,因此 self 在执行 block 中的指令时不会被释放。当这些指令完成时,strongSelf 将超出范围并释放对 self 的强引用。

这有时被称为“强势 self ,弱势 self 舞蹈”。

再次编辑:Swift 3 版本

class Some {}

class Test {
    var dict = [String: Some]()
    private let queue = DispatchQueue(label: "has", qos: .default, attributes: .concurrent)

    func has(key: String) -> Bool {
        var has = false
        queue.sync { [weak self] in
            guard let strongSelf = self else { return }

            has = strongSelf.dict[key] != nil
        }

        return has
    }

    func remove(key: String) {
        queue.async(flags: .barrier) { [weak self] in
            guard let strongSelf = self else { return }

            strongSelf.dict[key] = nil
        }
    }

    func add(key: String, ob: Some) {
        queue.async(flags: .barrier) { [weak self] in
            guard let strongSelf = self else { return }

            strongSelf.dict[key] = ob
        }
    }
}

关于multithreading - 对类中变量的线程安全访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27822117/

相关文章:

java - arraylist/atomic double array (Google Guava) 中的 .get() 操作是线程安全的吗?

ios - 如何在 Swift 中加载初始 View Controller ?

ios - Swift 中对于具有多个方面的模型的良好设计?

swift - 为什么我的 UIPickerview 无法在 UIAlertcontroller 中工作?

ios - 从 UTC-5 时区日期和时间 iPhone 应用程序获取当前 iPhone 设备时区日期和时间?

ios - 如何通知主MOC,后台MOC变化

c# - 执行 SQL 查询时显示进度条

java - 并发编程是更加网格化还是更加集群化?

java - 我是否需要在 future.get(); 之后使用 threadPool.awaitTermination

ios - 如何为我的所有日​​志添加 XCGLogger 前缀