首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在Swift中处理种族状况读写问题?

如何在Swift中处理种族状况读写问题?
EN

Stack Overflow用户
提问于 2018-08-30 12:52:09
回答 1查看 2.5K关注 0票数 7
  1. 我从Raywenderlich post 示例获得了一个带有调度屏障的并发队列 私有let concurrentPhotoQueue =DispatchQueue(标签:"com.raywenderlich.GooglyPuff.photoQueue",属性:.concurrent)

中完成写操作的地方

代码语言:javascript
运行
复制
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()
    }
  }
}

而阅读评论则是在

代码语言:javascript
运行
复制
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”)

代码语言:javascript
运行
复制
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正确。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-01-08 23:18:50

你问:

为什么读不完障碍.?

在这种读取器模式中,您不使用带“读”操作的屏障,因为允许读取与其他“读取”同时进行,而不会影响线程安全。这是读者-作者模式背后的整个激励思想,允许并发阅读。

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

总之,只有“写”需要与屏障一起进行(确保它们不是在任何“读”或“写”中并发完成)。但是,“阅读”不需要(或不需要)任何障碍。

为什么不..。用同步写?

您可以使用sync“编写”,但是,再说一遍,您为什么要这样做?这只会降低性能。让我们想象一下,您有一些尚未完成的读取,并且发出了一个带有障碍的“写”。调度队列将确保带有障碍的“写”不会与任何其他“读”或“写”同时发生,那么为什么发出“写”的代码坐在那里等待“写”完成呢?

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

你提到:

有了翻转行为,我得到了nil .

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

代码语言:javascript
运行
复制
public func object(forKey key: String) -> Any? {
    var result: Any?

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

    return result
}

这实际上是“设置一个名为result的变量,分派任务来异步检索它,但不要等到读取完成后才返回当前包含的result (即nil)”。

您可以理解为什么必须同步进行读取,因为在更新变量之前显然不能返回值!

因此,在重新处理后一个示例时,您可以在没有障碍的情况下同步读取,但是使用屏障异步编写:

代码语言:javascript
运行
复制
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:),我只写我自己的下标算子

代码语言:javascript
运行
复制
public subscript(key: String) -> Any? {
    get {
        concurrentQueue.sync { 
            dictionary[key] 
        } 
    }

    set { 
        concurrentQueue.async(flags: .barrier) {
            self.dictionary[key] = newValue
        }
    }
}

然后你就可以做这样的事情:

代码语言:javascript
运行
复制
store["Number"] = 10
print(store["Number"])
store["Number"] = 20
print(store["Number"])

注意,如果您发现这种读写器模式太复杂,请注意,您可以只使用一个串行队列(这就像对“读”和“写”都使用了一个屏障)。您可能仍然会做sync“读”和async“写”。这也很管用。但是在竞争激烈的“读”环境中,它只是比上面的读写器模式效率要低一点。

票数 12
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52097573

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档