线程安全访问类中的变量

时间:2015-01-07 14:55:06

标签: multithreading swift

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

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

在这种情况下,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
        }
    }
}

2 个答案:

答案 0 :(得分:7)

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

我会这样做的方式是这样的:

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指向{{} 1}}并且还创建一个强引用,以便在执行块中的指令时不会释放self。完成这些说明后,self将超出范围并释放对自我的强烈引用。

这有时被称为强大的自我,弱自我舞蹈"。

再次编辑:Swift 3版本

strongSelf

答案 1 :(得分:0)

这是另一个swift 3解决方案,它提供对AnyObject的线程安全访问。

它分配与' object'相关联的递归pthread_mutex。如果需要的话。

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(hostName: "stackoverflow.com", latency: 1)

<强>更新

您可以在swift文件中定义一个方法(不在类中):

/// Provides thread-safe access to given object
public func synchronizedAccess(to object: AnyObject, _ block: () -> Void)
{
    objc_sync_enter(object)
    block()
    objc_sync_exit(object)
}

并像这样使用它:

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