问题描述
我有一个应用程序,它有一个单例,用于存储整个应用程序的信息.但是,当使用来自不同线程的单例时,这会产生一些数据竞争问题.
I have and Application which has a singleton that stores information across the whole app. However, this is creating some data race issues when using the singleton from different threads.
这里有一个非常虚拟和简单的问题版本:
Here there is a very dummy and simplistic version of the problem:
单身
class Singleton {
static var shared = Singleton()
var foo: String = "foo"
}
单例的使用(为了简单起见,来自 AppDelegate)
Use of the singleton (from the AppDelegate for simplicity)
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
}
}
有什么方法可以确保单例是线程安全的,这样它就可以在应用程序的任何地方使用,而不必担心您在哪个线程中?
Is there any way to ensure that a singleton is thread safe, so it can be used from anywhere in the app without having to worry about which thread you are in?
这个问题不是重复在 Swift 中使用 dispatch_once 单例模型 因为(如果我理解正确的话)在那里他们解决了访问单例对象本身的问题,但不能确保其属性的读取和写入是线程安全的.
This question is not a duplicate of Using a dispatch_once singleton model in Swift since (if I understood it correctly) in there they are addressing the problem of accessing to the singleton object itself, but not ensuring that the reading and writing of its properties is done thread safely.
推荐答案
感谢@rmaddy 的评论为我指明了正确的方向,我能够解决问题.
Thanks to @rmaddy comments which pointed me in the right direction I was able to solve the problem.
为了使Singleton
线程的foo
属性安全,需要修改如下:
In order to make the property foo
of the Singleton
thread safe, it need to be modified as follows:
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
属性.
Thread safety is accomplished by having a computed property foo
which uses an internalQueue
to access the "real" _foo
property.
此外,为了获得更好的性能,internalQueue
被创建为并发.这意味着在写入属性时需要添加 barrier
标志.
Also, in order to have better performance internalQueue
is created as concurrent. And it means that it is needed to add the barrier
flag when writing to the property.
barrier
标志的作用是确保在队列中所有先前安排的工作项都完成后执行工作项.
What the barrier
flag does is to ensure that the work item will be executed when all previously scheduled work items on the queue have finished.
这篇关于swift中的线程安全单例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!