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
用于读取共享资源-具有无回调的清晰APIbarrier 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() {
}
}