3

最近我正在阅读流行的图像缓存库Kingfisher的代码。

我对ImageDownloader. 在该下载器中,所有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()
        }
    }
}

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

4

1 回答 1

3

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

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

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

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

于 2018-03-30T18:30:09.673 回答