最近,我正在阅读热门图片缓存库Kingfisher的代码。
我对ImageDownloader
上的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()
}
}
}
每个屏障操作都会相互阻塞,使队列实际上成为一个序列。那么,为什么翠鸟使用concurrent
队列而不是serial
队列?
答案 0 :(得分:1)
在某些情况下,在concurrent
队列中使用serial
队列可能有助于GCD线程安全。对于类中的所有操作,串行执行可能不是所希望的或必需的。例如,对于不修改状态的“读取”操作,它们有意义地同时执行并发。即,如果“读取”操作返回的信息不依赖于您正在等待的其他操作可能修改的状态,则没有理由等待串行执行。
因此,在这些情况下,您可以像Kingfisher那样使用concurrent
队列,并为任何改变数据状态的操作设置.barrier
标志,以告诉块等待队列中的所有其他操作在执行之前完成。
我不能说明Kingfisher使用concurrent
队列背后的具体原因,但只是想注意一个例子用例,当你可能更喜欢concurrent
而不是serial
时GCD队列安全。使用concurrent
可以提供额外的灵活性,因为您可以决定是应该同时执行操作还是使用.barrier标志“连续”执行操作,具体取决于行为。
有关此模式的说明,请查看此处的.barrier
说明:http://khanlou.com/2016/04/the-GCD-handbook/