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

Multithreading 对类中变量的线程安全访问,multithreading,swift,Multithreading,Swift,在一个可能有多个线程运行的应用程序中,为了安全起见,我不确定这些方法是否可以在多线程环境下访问,我已经做了一个测试类来演示这种情况 一种方法已经被编程为线程安全(如果做得对,也请评论),但其他方法没有 在这种情况下,remove和add中只有一行代码,是否有必要使它们成为线程安全的,还是会被夸大 import Foundation class Some {} class Test { var dict = [String: Some]() func has(key: Str

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

一种方法
已经
被编程为线程安全(如果做得对,也请评论),但其他方法没有

在这种情况下,
remove
add
中只有一行代码,是否有必要使它们成为线程安全的,还是会被夸大

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
        }
    }
}

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

我会这样做:

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
        }
    }
}
首先,我将创建一个串行队列,该队列将作为对象的属性用于访问字典,而不是每次都创建一个新队列。队列是私有的,因为它只在内部使用

当我想从类中获取一个值时,我只是同步地向队列分配一个块,并等待块完成,然后返回队列是否存在。因为这不会改变字典,所以在并发队列上运行这种类型的多个块是安全的

当我想从字典中添加或删除值时,我将块添加到队列中,但带有一个屏障。这将在队列运行时停止队列上的所有其他块。完成后,所有其他块都可以并发运行。我使用的是异步调度,因为我不需要等待返回值

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

通过这种方式,您可以在获取值时快速方便地并发运行,并且在字典发生变化时阻塞线程是安全的

编辑以添加

self
在块中标记为
weak
,这样就不会创建参考循环。正如评论中提到的@MartinR;对象可能在块仍在队列中时被解除分配,如果发生这种情况,则self未定义,并且您可能会在尝试访问字典时遇到运行时错误,因为它也可能被解除分配

通过将块内的声明
self
设置为弱,如果对象存在,则
self
将不会为零,并且可以有条件地展开为
strongSelf
,指向
self
,并创建强引用,这样在执行块中的指令时,
self
将不会被解除分配。当这些说明完成后,
strongSelf
将超出范围,并释放对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
        }
    }
}

这里是另一个swift 3解决方案,它提供对任何对象的线程安全访问

如果需要,它将分配与“对象”关联的递归pthread_互斥体

class LatencyManager
{
    private var latencies = [String : TimeInterval]()

    func set(hostName: String, latency: TimeInterval) {
        synchronizedBlock(lockedObject: latencies as AnyObject) { [weak self] in
            self?.latencies[hostName] = latency
        }
    }

    /// Provides thread-safe access to given object
    private func synchronizedBlock(lockedObject: AnyObject, block: () -> Void) {
        objc_sync_enter(lockedObject)
        block()
        objc_sync_exit(lockedObject)
    }
}
然后您可以调用例如
set(主机名:“stackoverflow.com”,延迟:1)

更新

您只需在swift文件(而不是类)中定义方法即可:

然后像这样使用它:

synchronizedAccess(to: myObject) {
    myObject.foo()
 }

回答得好!但有一个问题是:
[弱自我]
会不会更安全?执行屏障块时,
Test
对象可能会被释放。@MartinR在这种情况下,块运行的队列不也会被释放吗?从:“队列由系统保留,直到块运行完成。”–因此(我认为)即使对象被解除分配并释放对队列的引用,队列也将继续运行,直到所有未完成的块都完成为止。@Abizern如果我还有另一个字典,比如说
dict2
,我是否应该为
dict2
s
声明另一个
dispatch\u queue\t
变量,
删除
添加
functions@user2727195如果您有另一个字典,那么使用不同的队列是一个好主意。阻止一个字典的getter是没有意义的,因为您正在变异另一个字典。
synchronizedAccess(to: myObject) {
    myObject.foo()
 }