swift中的线程安全单例

jmo0nnb3  于 2023-01-19  发布在  Swift
关注(0)|答案(2)|浏览(159)

我有一个应用程序,它有一个在整个应用程序中存储信息的单例。但是,当从不同的线程使用单例时,这会产生一些数据争用问题。
这里有一个非常虚假和简单化的版本的问题:

单身

class Singleton {
    static var shared = Singleton()

    var foo: String = "foo"
}

使用单例(为简单起见,来自AppDelegate)

class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        DispatchQueue.global().async {
            var foo = Singleton.shared.foo // Causes data race
        }

        DispatchQueue.global().async {
            Singleton.shared.foo = "bar"   // Causes data race
        }

        return true
    }
}

有没有什么方法可以确保单例是线程安全的,这样就可以在应用程序的任何地方使用它,而不必担心你在哪个线程中?
这个问题不是Using a dispatch_once singleton model in Swift的副本,因为(如果我理解正确的话)在那里他们解决了访问单例对象本身的问题,但没有确保其属性的阅读是线程安全的。

eit6fx6z

eit6fx6z1#

多亏了@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来访问“真实的”_foo属性。
另外,为了获得更好的性能,internalQueue被创建为并发的,这意味着在写入属性时需要添加barrier标志。
barrier标志的作用是确保在队列中所有以前计划的工作项都完成时执行该工作项。

8e2ybdfx

8e2ybdfx2#

Swift线程安全单例

iOS Thread safe(https://stackoverflow.com/a/65263893/4770877)
GCD(https://stackoverflow.com/a/61102990/4770877)
[用于线程安全的Swift屏障标志]
您可以使用GCD和3个主要内容来实现Swift的Singleton并发环境模式:
1.自定义并发队列-本地队列可在同时进行多个读取的情况下提高性能

  1. sync-customQueue.sync用于阅读共享资源-具有清晰的API,无需回调
  2. barrier flag-customQueue.async(flags: .barrier)用于写入操作:运行操作完成后等待-〉执行写入任务-〉继续执行任务
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() {
    }
}

相关问题