最近在看流行的图片缓存库的代码Kingfisher .
我对 ImageDownloader
上的 GCD 用法感到困惑。在该下载器中,所有与 ImageFetchLoad
(获取图像的任务)相关的操作都被分派(dispatch)到一个名为 barrierQueue
的并发队列:
barrierQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Barrier.\(name)", attributes: .concurrent)
令人困惑的部分是所有操作都是使用屏障同步分派(dispatch)的:
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()
}
}
}
每个屏障操作都会相互阻塞,这使得队列实际上是一个串行队列。因此,为什么翠鸟使用并发
队列而不是串行
队列?
最佳答案
在某些情况下,为了 GCD 线程安全,使用 concurrent
队列而不是 serial
队列可能是有意义的。对于类中的所有操作,串行执行可能不是理想的或必需的。例如,对于不修改状态的“读取”操作,它们并发 和同步执行是有意义的。即,如果“读取”操作返回的信息不依赖于可能被您正在等待的其他操作修改的状态,则没有理由等待串行执行。
因此在这些情况下,您可以像 Kingfisher 那样使用concurrent
队列,并为任何改变数据状态的操作设置一个 .barrier
标志以告知 block 等待直到队列中的所有其他操作都完成后才执行。
我无法说明 Kingfisher 使用 concurrent
队列背后的具体原理,但只是想说明一个示例用例,说明何时您可能更喜欢 concurrent
serial
用于 GCD 队列安全。使用 concurrent
可以提供额外的灵 active ,因为您可以根据行为决定操作是并发执行还是使用 .barrier 标志“串行”执行。
有关此模式的描述,请查看此处的 .barrier
描述:http://khanlou.com/2016/04/the-GCD-handbook/
关于ios - 只有屏障任务的并发队列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49576345/