【问题标题】:Concurrent queue with barrier task only仅具有屏障任务的并发队列
【发布时间】:2018-03-30 14:34:33
【问题描述】:

最近在看流行的图片缓存库Kingfisher的代码。

我对@9​​87654324@ 上的 GCD 用法感到困惑。在那个下载器中,所有与ImageFetchLoad(获取图像的任务)相关的操作都被分派到一个名为barrierQueue的并发队列中:

barrierQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Barrier.\(name)", attributes: .concurrent)

令人困惑的部分是所有操作都是使用屏障同步调度的:

barrierQueue.sync(flags: .barrier) {
    if let URL = task.internalTask.originalRequest?.url, let imageFetchLoad = self.fetchLoads[URL] {
        imageFetchLoad.downloadTaskCount -= 1
        if imageFetchLoad.downloadTaskCount == 0 {
            task.internalTask.cancel()
        }
    }
}

每个屏障操作都会相互阻塞,这使得队列实际上是一个串行的。因此,为什么 Kingfisher 使用 concurrent 队列而不是 serial 队列?

【问题讨论】:

  • 当它可能只是历史时,不要认为有深层原因。您是否联系过添加标志的开发人员? (onevcat: github.com/onevcat/Kingfisher/commit/…) 特别注意,在那次提交之前,有些使用了.barrier,有些没有:github.com/onevcat/Kingfisher/blob/…
  • 根据我的理解障碍在并发队列中是有意义的,不确定在串行队列中是否有意义,因为无论如何任务都会一个接一个地执行。

标签: ios swift multithreading grand-central-dispatch kingfisher


【解决方案1】:

在某些情况下,为 GCD 线程安全使用 concurrent 队列而不是 serial 队列可能是有意义的。对于一个类中的所有操作,串行执行可能不是可取的或必要的。例如对于不修改状态的“读取”操作,它们并发和同步执行是有意义的。即,如果“读取”操作返回的信息不依赖于可能被您正在等待的其他操作修改的状态,则没有理由等待串行执行。

因此,在这些情况下,您可以像 Kingfisher 一样使用 concurrent 队列,并为任何改变数据状态的操作设置 .barrier 标志,以告诉块等待队列中的所有其他操作完成后再执行.

我无法说明 Kingfisher 使用 concurrent 队列背后的具体理由,但只想指出一个示例用例,即您可能更喜欢 concurrent 而不是 serial 以实现 GCD 队列安全。使用concurrent 可以提供额外的灵活性,因为您可以根据行为决定是应该使用 .barrier 标志同时执行操作还是“串行”执行操作。

有关此模式的描述,请查看此处的 .barrier 描述:http://khanlou.com/2016/04/the-GCD-handbook/

【讨论】:

    猜你喜欢
    • 2020-02-02
    • 2019-12-23
    • 1970-01-01
    • 1970-01-01
    • 2019-08-21
    • 1970-01-01
    • 2019-06-12
    • 2016-08-28
    • 1970-01-01
    相关资源
    最近更新 更多