中完成写操作的地方
func addPhoto(_ photo: Photo) {
concurrentPhotoQueue.async(flags: .barrier) { [weak self] in
// 1
guard let self = self else {
return
}
// 2
self.unsafePhotos.append(photo)
// 3
DispatchQueue.main.async { [weak self] in
self?.postContentAddedNotification()
}
}
}而阅读评论则是在
var photos: [Photo] {
var photosCopy: [Photo]!
// 1
concurrentPhotoQueue.sync {
// 2
photosCopy = self.unsafePhotos
}
return photosCopy
}因为这将解决种族条件。这里,为什么只有写操作是用屏障完成的,而在Sync中读取。为什么读不是用障碍完成,而是用同步写的?与同步写入一样,它将等待读起来像锁一样,而屏障读取时它只会被读取操作。
集合(10,forKey:"Number") 打印(forKey:“Number”) 集合(20,forKey:"Number") 打印(forKey:“Number”)
public func set(_ value: Any?, forKey key: String) {
concurrentQueue.sync {
self.dictionary[key] = value
}
}
public func object(forKey key: String) -> Any? {
// returns after concurrentQueue is finished operation
// beacuse concurrentQueue is run synchronously
var result: Any?
concurrentQueue.async(flags: .barrier) {
result = self.dictionary[key]
}
return result
}与翻转行为,我得到零两次,与障碍在写它是给予10和20正确。
发布于 2019-01-08 23:18:50
你问:
为什么读不完障碍.?
在这种读取器模式中,您不使用带“读”操作的屏障,因为允许读取与其他“读取”同时进行,而不会影响线程安全。这是读者-作者模式背后的整个激励思想,允许并发阅读。
因此,您可以使用带“read”的屏障(它仍然是线程安全的),但如果同时调用多个“读取”请求,则会不必要地对性能产生负面影响。如果两个“读”操作可以同时进行,为什么不让它们同时进行呢?除非你绝对需要,否则不要使用障碍(降低性能)。
总之,只有“写”需要与屏障一起进行(确保它们不是在任何“读”或“写”中并发完成)。但是,“阅读”不需要(或不需要)任何障碍。
为什么不..。用同步写?
您可以使用sync“编写”,但是,再说一遍,您为什么要这样做?这只会降低性能。让我们想象一下,您有一些尚未完成的读取,并且发出了一个带有障碍的“写”。调度队列将确保带有障碍的“写”不会与任何其他“读”或“写”同时发生,那么为什么发出“写”的代码坐在那里等待“写”完成呢?
使用sync进行写操作只会对性能产生负面影响,而不会带来任何好处。问题不是“为什么不用sync编写?”而是“为什么要用sync编写?”对后一个问题的答案是,你不想不必要地等待。当然,你必须等待“读”,而不是“写”。
你提到:
有了翻转行为,我得到了
nil.
是的,那么让我们考虑一下您假设的使用async的“阅读”操作
public func object(forKey key: String) -> Any? {
var result: Any?
concurrentQueue.async {
result = self.dictionary[key]
}
return result
}这实际上是“设置一个名为result的变量,分派任务来异步检索它,但不要等到读取完成后才返回当前包含的result (即nil)”。
您可以理解为什么必须同步进行读取,因为在更新变量之前显然不能返回值!
因此,在重新处理后一个示例时,您可以在没有障碍的情况下同步读取,但是使用屏障异步编写:
public func object(forKey key: String) -> Any? {
return concurrentQueue.sync {
self.dictionary[key]
}
}
public func set(_ value: Any?, forKey key: String) {
concurrentQueue.async(flags: .barrier) {
self.dictionary[key] = value
}
}注意,因为“read”操作中的sync方法将返回闭包返回的任何内容,因此可以简化代码,如上面所示。
或者,我个人,而不是object(forKey:)和set(_:forKey:),我只写我自己的下标算子
public subscript(key: String) -> Any? {
get {
concurrentQueue.sync {
dictionary[key]
}
}
set {
concurrentQueue.async(flags: .barrier) {
self.dictionary[key] = newValue
}
}
}然后你就可以做这样的事情:
store["Number"] = 10
print(store["Number"])
store["Number"] = 20
print(store["Number"])注意,如果您发现这种读写器模式太复杂,请注意,您可以只使用一个串行队列(这就像对“读”和“写”都使用了一个屏障)。您可能仍然会做sync“读”和async“写”。这也很管用。但是在竞争激烈的“读”环境中,它只是比上面的读写器模式效率要低一点。
https://stackoverflow.com/questions/52097573
复制相似问题