swift中的线程安全单例

swift中的线程安全单例,swift,thread-safety,swift4,Swift,Thread Safety,Swift4,我有一个应用程序,它有一个单例,在整个应用程序中存储信息。但是,当使用来自不同线程的单例时,这会产生一些数据竞争问题 这里有一个非常虚拟和简单的问题版本: 单身人士 class Singleton { static var shared = Singleton() var foo: String = "foo" } 单例的使用(为了简单起见,请参见AppDelegate) 有没有办法确保单例是线程安全的,这样它就可以在应用程序中的任何地方使用,而不必担心你在哪个线程中 这个问

我有一个应用程序,它有一个单例,在整个应用程序中存储信息。但是,当使用来自不同线程的单例时,这会产生一些数据竞争问题

这里有一个非常虚拟和简单的问题版本:

单身人士

class Singleton {
    static var shared = Singleton()

    var foo: String = "foo"
}
单例的使用(为了简单起见,请参见AppDelegate)

有没有办法确保单例是线程安全的,这样它就可以在应用程序中的任何地方使用,而不必担心你在哪个线程中


这个问题是而不是的重复,因为(如果我理解正确的话)在这里,他们解决了访问单例对象本身的问题,但没有确保读取和写入其属性是线程安全的

多亏@rmaddy的评论为我指明了正确的方向,我才得以解决问题

为了使
Singleton
的属性
foo
线程安全,需要对其进行如下修改:

    class Singleton {

    static let shared = Singleton()

    private init(){}

    private let internalQueue = DispatchQueue(label: "com.singletioninternal.queue",
                                              qos: .default,
                                              attributes: .concurrent)

    private var _foo: String = "aaa"

    var foo: String {
        get {
            return internalQueue.sync {
                _foo
            }
        }
        set (newState) {
            internalQueue.async(flags: .barrier) {
                self._foo = newState
            }
        }
    }

    func setup(string: String) {
        foo = string
    }
}
线程安全是通过使用计算属性
foo
来实现的,该属性使用
internalQueue
来访问“real”
\u foo
属性

此外,为了获得更好的性能,
内部队列
被创建为并发队列。这意味着在写入属性时需要添加
barrier
标志

barrier
标志的作用是确保在队列上所有先前计划的工作项都已完成时执行工作项。

您可以使用
GCD
和3个主要功能为并发环境实现Swift的单例模式:

  • 自定义并发队列-本地队列,可在同时进行多个读取时获得更好的性能
  • sync
    -
    customQueue.sync
    用于读取共享资源-具有无回调的清晰API
  • barrier flag
    -
    customQueue.async(flags:.barrier)
    对于写入操作:运行操作完成时等待->执行写入任务->继续执行任务

  • 请参阅示例解决方案。这似乎工作得很好。谢谢你给我指明了正确的方向!这里有一个更好的解决方案(参见使用barrier sync进行写入的解决方案):可能是@sundance的副本-他不担心与singleton对象本身的任何竞争。他担心同步访问singleton的属性,这是另一个问题。很好的QA,nikano!如果同一个单例具有成员foo2,您是使用相同的内部队列保护它,还是使用不同的内部队列保护它?@nikano类
    单例
    使用静态
    共享:单例
    实例,该实例在类
    单例
    的所有实例中都是相同的。但是
    internalQueue
    对象在
    Singleton
    的每个实例中都是不同的。iOS如何知道提交到不同
    internalQueue
    对象的块应该序列化以进行写入?Apple文档暗示,
    标签
    仅用于调试。
    internalQueue
    也应该是
    static
    吗?为什么不使用串行队列,而是使用并发队列呢?因为串行队列不支持同时进行多个读取。因为我们不需要一次只阻止一次读取,所以我们使用并发队列来允许多个读取。但是,我们确实需要一次只支持一次写入,所以我们使用了barrier标志。
        class Singleton {
    
        static let shared = Singleton()
    
        private init(){}
    
        private let internalQueue = DispatchQueue(label: "com.singletioninternal.queue",
                                                  qos: .default,
                                                  attributes: .concurrent)
    
        private var _foo: String = "aaa"
    
        var foo: String {
            get {
                return internalQueue.sync {
                    _foo
                }
            }
            set (newState) {
                internalQueue.async(flags: .barrier) {
                    self._foo = newState
                }
            }
        }
    
        func setup(string: String) {
            foo = string
        }
    }
    
    public class MySingleton {
    
        public static let shared = Singleton()
        
        //1. custom queue
        private let customQueue = DispatchQueue(label: "com.mysingleton.queue", qos: .default, attributes: .concurrent)
        //shared resource
        private var sharedResource: String = "Hello World"
    
        //computed property can be replaced getters/setters
        var computedProperty: String {
            get {
                //2. sync read
                return customQueue.sync {
                    sharedResource
                }
            }
            
            set {
                //3. async write
                customQueue.async(flags: .barrier) {
                    sharedResource = newValue
                }
            }
        }
        
        private init() {
        }
    }