sal*_*qui 4 concurrency semaphore grand-central-dispatch ios swift
我从Raywenderlich帖子示例中得到了一个带有调度屏障的并发队列
private let concurrentPhotoQueue = DispatchQueue(label: "com.raywenderlich.GooglyPuff.photoQueue", attributes: .concurrent)
写操作在哪里完成
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()
}
}
}
Run Code Online (Sandbox Code Playgroud)
虽然读取操作是在
var photos: [Photo] {
var photosCopy: [Photo]!
// 1
concurrentPhotoQueue.sync {
// 2
photosCopy = self.unsafePhotos
}
return photosCopy
}
Run Code Online (Sandbox Code Playgroud)
因为这将解决竞争条件。这里为什么只有Write操作是通过barrier和Read in Sync 完成的。为什么 Read 不是用屏障完成,而是用同步写?。与同步写入一样,它会像锁一样等待直到读取,而屏障读取只会进行读取操作。
设置(10,forKey:“数字”)
打印(对象(forKey:“数字”))
设置(20,forKey:“数字”)
打印(对象(forKey:“数字”))
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
}
Run Code Online (Sandbox Code Playgroud)
使用翻转行为,我两次都为零,写入障碍给出 10 和 20 正确
你问:
为什么 Read 没有用屏障完成...?。
在这种读写器模式,您不使用屏障“读”操作,因为读取被允许相对于其他同时发生的“读”,不影响线程安全。这是读写模式背后的全部动机,允许并发读取。
因此,您可以对“读取”使用屏障(它仍然是线程安全的),但如果碰巧同时调用多个“读取”请求,则会对性能产生不必要的负面影响。如果两个“读”操作可以同时发生,为什么不让它们发生呢?除非绝对需要,否则不要使用障碍(降低性能)。
最重要的是,只有“写入”需要与屏障一起发生(确保它们不会与任何“读取”或“写入”同时进行)。但是“读取”不需要(或不希望)任何障碍。
[为什么不] ...同步写入?
你可以用 来“写” sync,但是,你为什么要这样写?它只会降低性能。假设您有一些尚未完成的读取,并且您使用屏障分派了“写入”。调度队列将为我们确保使用屏障调度的“写入”不会与任何其他“读取”或“写入”同时发生,那么为什么调度该“写入”的代码应该坐在那里等待“写”要完成吗?
使用syncfor write 只会对性能产生负面影响,而不会带来任何好处。问题不是“为什么不写sync?” 而是“为什么你会想用写的sync?” 后一个问题的答案是,您不想不必要地等待。当然,您必须等待“读取”,而不是“写入”。
你提到:
随着翻转行为,我越来越
nil......
是的,让我们考虑一下您假设的“读取”操作async:
public func object(forKey key: String) -> Any? {
var result: Any?
concurrentQueue.async {
result = self.dictionary[key]
}
return result
}
Run Code Online (Sandbox Code Playgroud)
这个有效的意思是“设置一个名为 的变量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
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,因为sync“读取”操作中的方法将返回闭包返回的任何内容,因此您可以大大简化代码,如上所示。
或者,就我个人而言,我只是编写自己的下标运算符,而不是object(forKey:)and :set(_:forKey:)
public subscript(key: String) -> Any? {
get {
concurrentQueue.sync {
dictionary[key]
}
}
set {
concurrentQueue.async(flags: .barrier) {
self.dictionary[key] = newValue
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以做这样的事情:
store["Number"] = 10
print(store["Number"])
store["Number"] = 20
print(store["Number"])
Run Code Online (Sandbox Code Playgroud)
请注意,如果您发现这种读写器模式过于复杂,请注意您可以只使用串行队列(这就像对“读取”和“写入”使用屏障一样)。您仍然可能会sync“读取”和async“写入”。这也有效。但是在“读取”争用较多的环境中,它的效率仅比上述读写器模式低一点。
| 归档时间: |
|
| 查看次数: |
1438 次 |
| 最近记录: |