线程安全访问类中的变量 [英] Thread safe access to a variable in a class

查看:59
本文介绍了线程安全访问类中的变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

一种方法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
        }
    }
}

解决方案

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

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

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不会为nil,可以有条件地展开为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
        }
    }
}

in an application where there could be multiple threads running, and not sure about the possibilities if these methods will be accessed under a multhreaded environment or not but to be safe, I've done a test class to demonstrate a situation.

One method has was programmed to be thread safe (please also comment if it's done right) but the rest were not.

In a situation like this, where there is only one single line of code inside remove and add, is it necessary to make them thread safe or is it going to be exaggeration.

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

Edit after comments

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

解决方案

The way you are checking whether a key exists is incorrect. You are creating a new queue every time, which means the operations are not happening synchronously.

The way I would do it is like so:

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

Firstly, I am creating a serial queue that is going to be used to access the dictionary as a property of the object, rather than creating a new one every time. The queue is private as it is only used internally.

When I want to get a value out of the class, I am just dispatching a block synchronously to the queue and waits for the block to finish before returning whether or not the queue exists. Since this is not mutating the dictionary, it is safe for multiple blocks of this sort to run on the concurrent queue.

When I want to add or remove values from the dictionary, I am adding the block to the queue but with a barrier. What this does is that it stops all other blocks on the queue while it is running. When it is finished, all the other blocks can run concurrently. I am using an async dispatch, because I don't need to wait for a return value.

Imagine you have multiple threads trying to see whether or not key values exist or adding or removing values. If you have lots of reads, then they happen concurrently, but when one of the blocks is run that will change the dictionary, all other blocks wait until this change is completed and then start running again.

In this way, you have the speed and convenience of running concurrently when getting values, and the thread safety of blocking while the dictionary is being mutated.

Edited to add

self is marked as weak in the block so that it doesn't create a reference cycle. As @MartinR mentioned in the comments; it is possible that the object is deallocated while blocks are still in the queue, If this happens then self is undefined, and you'll probably get a runtime error trying to access the dictionary, as it may also be deallocated.

By setting declaring self within the block to be weak, if the object exists, then self will not be nil, and can be conditionally unwrapped into strongSelf which points to self and also creates a strong reference, so that self will not be deallocated while the instructions in the block are carried out. When these instructions complete, strongSelf will go out of scope and release the strong reference to self.

This is sometimes known as the "strong self, weak self dance".

Edited Again : Swift 3 version

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

这篇关于线程安全访问类中的变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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