Недавно читаю коды популярной библиотеки кеширования изображений Зимородок.
Я не понимаю, как используется GCD на 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()
}
}
}
Каждая операция барьера будет блокировать друг друга, что делает очередь фактически последовательной. Так почему же Kingfisher использует очередь concurrent вместо serial?
Насколько я понимаю, барьер будет иметь смысл в параллельной очереди, не уверен, будет ли это иметь смысл в последовательной очереди, потому что задачи в любом случае будут выполняться одна за другой.





В некоторых случаях может иметь смысл использовать очередь concurrent поверх очереди serial для обеспечения безопасности потоков GCD. Последовательное выполнение может быть нежелательным или необходимым для всех операций в классе. Например, для операций «чтения», которые не изменяют состояние, им имеет смысл выполнять одновременно и синхронно. то есть, если операция «чтения» возвращает информацию, не зависящую от состояния, которое может быть изменено другими операциями, которые вы ожидаете, нет причин ждать последовательного выполнения.
Таким образом, в этих случаях вы можете использовать очередь concurrent, как это делает Kingfisher, и установить флаг .barrier для любых операций, которые изменяют состояние данных, чтобы сообщить блоку, чтобы он дождался завершения всех других операций в очереди перед выполнением.
Я не могу говорить о конкретном обосновании использования Kingfisher очереди concurrent, но просто хотел отметить пример использования, когда вы могли бы предпочесть concurrentserial для безопасности очереди GCD. Использование concurrent может обеспечить дополнительную гибкость, поскольку вы можете решить, должны ли операции выполняться одновременно или «последовательно» с флагом .barrier, в зависимости от поведения.
Описание этого шаблона можно найти в описании .barrier здесь: http://khanlou.com/2016/04/the-GCD-handbook/
Не думайте, что есть серьезная причина, когда это может быть просто история. Вы связались с разработчиком, который добавил флаг? (onevcat: github.com/onevcat/Kingfisher/commit/…) Особо обратите внимание, что до этого коммита некоторые использовали
.barrier, а некоторые нет: github.com/onevcat/Kingfisher/blob/…