我有和应用程序有一个单一的存储整个应用程序的信息.但是,当使用来自不同线程的单例时,这会产生一些数据争用问题.
这里有一个非常虚拟和简单化的问题版本:
独生子
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的重复(因为我理解正确),因为它们正在解决访问单例对象本身的问题,但不能确保其属性的读取和写入是安全地完成的.
感谢@rmaddy的评论指出了我正确的方向,我能够解决问题.
为了使Singleton线程的属性foo安全,需要修改如下:
class Singleton {
static var shared = Singleton()
private let internalQueue = DispatchQueue(label: "SingletionInternalQueue",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”_foo属性.
此外,为了获得更好的性能,internalQueue被创建为并发.这意味着在写入属性时需要添加barrier标志.
屏障标志的作用是确保在队列中所有先前计划的工作项完成时执行工作项.

