我有一个应用程序,它有一个在整个应用程序中存储信息的单例。但是,当从不同的线程使用单例时,这会产生一些数据争用问题。
这里有一个非常虚假和简单化的版本的问题:
单身
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的副本,因为(如果我理解正确的话)在那里他们解决了访问单例对象本身的问题,但没有确保其属性的阅读是线程安全的。
2条答案
按热度按时间eit6fx6z1#
多亏了@rmaddy的评论给我指出了正确的方向,我才得以解决这个问题。
为了使
Singleton
的属性foo
线程安全,需要对其进行如下修改:线程安全是通过具有计算属性
foo
来实现的,该计算属性使用internalQueue
来访问“真实的”_foo
属性。另外,为了获得更好的性能,
internalQueue
被创建为并发的,这意味着在写入属性时需要添加barrier
标志。barrier
标志的作用是确保在队列中所有以前计划的工作项都完成时执行该工作项。8e2ybdfx2#
Swift线程安全单例
iOS Thread safe(https://stackoverflow.com/a/65263893/4770877)
GCD(https://stackoverflow.com/a/61102990/4770877)
[用于线程安全的Swift屏障标志]
您可以使用
GCD
和3个主要内容来实现Swift的Singleton并发环境模式:1.自定义并发队列-本地队列可在同时进行多个读取的情况下提高性能
sync
-customQueue.sync
用于阅读共享资源-具有清晰的API,无需回调barrier flag
-customQueue.async(flags: .barrier)
用于写入操作:运行操作完成后等待-〉执行写入任务-〉继续执行任务