gpt4 book ai didi

ios - 如何处理 Swift 中的竞争条件读/写问题?

转载 作者:可可西里 更新时间:2023-11-01 01:56:52 25 4
gpt4 key购买 nike

  1. 我从 Raywenderlich post Example

    得到了一个带有调度屏障的并发队列

    private let concurrentPhotoQueue = DispatchQueue(标签:“com.raywenderlich.GooglyPuff.photoQueue”,属性:.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()
}
}
}

虽然读取操作在

中完成
var photos: [Photo] {
var photosCopy: [Photo]!

// 1
concurrentPhotoQueue.sync {

// 2
photosCopy = self.unsafePhotos
}
return photosCopy
}

因为这将解决竞争条件。这就是为什么在Sync 中只有Write 操作与barrierRead 一起完成。为什么 Read 没有完成 barrier 和 write with sync ?与Sync Write一样,它会像锁一样等待读取,而barrier Read只会进行读取操作。

set(10, forKey: "Number")

print(object(forKey: "Number"))

set(20, forKey: "Number")

print(object(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
}

通过翻转行为,我两次都得到 nil,Write 上有障碍,它给出了 10 和 20 正确

最佳答案

你问:

Why is Read not done with barrier ... ?.

在这种读写器模式中,您不对“读取”操作使用屏障,因为读取允许与其他“读取”并发发生,而不会影响线程安全。允许并发读取是读写器模式背后的全部激励思想。

因此,您可以对“读取”使用屏障(它仍然是线程安全的),但如果恰好同时调用多个“读取”请求,它会对性能产生不必要的负面影响时间。如果两个“读”操作可以同时发生,为什么不让它们发生呢?除非绝对需要,否则不要使用障碍(降低性能)。

最重要的是,只有“写入”需要与屏障一起发生(确保它们不会与任何“读取”或“写入”同时完成)。但是“读取”不需要(或不需要)障碍。

[Why not] ... write with sync?

可以使用sync“写入”,但是,再说一次,您为什么要这么做?它只会降低性能。假设您有一些尚未完成的读取,并且您发送了一个带有屏障的“写入”。调度队列将为我们确保使用屏障调度的“写入”不会与任何其他“读取”或“写入”同时发生,那么为什么调度“写入”的代码应该坐在那里等待“写”完了?

使用sync 进行写入只会对性能产生负面影响,并没有任何好处。问题不是“为什么不用 sync 来写?”而是“为什么你想要sync来写?”后一个问题的答案是,您不想不必要地等待。当然,您必须等待“读取”,而不是“写入”。

你提到:

With the flip behavior, I am getting nil ...

是的,让我们考虑一下您假设的使用 async 的“读取”操作:

public func object(forKey key: String) -> Any? {
var result: Any?

concurrentQueue.async {
result = self.dictionary[key]
}

return result
}

这实际上是说“设置一个名为 result 的变量,分派(dispatch)任务以异步方式检索它,但不要等待读取完成才返回任何 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
}
}

请注意,因为“读取”操作中的 sync 方法将返回闭包返回的任何内容,所以您可以大大简化代码,如上所示。

或者,就我个人而言,与其使用 object(forKey:)set(_:forKey:),不如我自己写 subscript operator :

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“写入”。那也行。但在“读取”竞争激烈的环境中,它的效率略低于上述读写器模式。

关于ios - 如何处理 Swift 中的竞争条件读/写问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52097573/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com