Параллельная очередь только с барьерной задачей

Недавно я читаю коды популярной библиотеки кэширования изображений Kingfisher.

Я запутался в использовании 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?


person kukushi    schedule 30.03.2018    source источник
comment
Не думайте, что есть глубокая причина, когда это может быть просто история. Вы связались с разработчиком, который добавил флаг? (onevcat: github.com/onevcat/Kingfisher/commit/) Особо обратите внимание, что перед этот коммит, некоторые использовали .barrier, а некоторые нет: github.com/ onevcat/Kingfisher/blob/   -  person Rob Napier    schedule 30.03.2018
comment
Насколько я понимаю, барьер имел бы смысл в параллельной очереди, но не уверен, что это имело бы смысл в последовательной очереди, потому что задачи в любом случае выполнялись бы одна за другой.   -  person user1046037    schedule 30.03.2018


Ответы (1)


В некоторых случаях имеет смысл использовать очередь concurrent вместо очереди serial для обеспечения безопасности потоков GCD. Последовательное выполнение может быть нежелательным или необходимым для всех операций в классе. Например, для операций чтения, которые не изменяют состояние, имеет смысл выполнять их параллельно и синхронно. т. е., если операция «чтения» возвращает информацию, не зависящую от состояния, которое может быть изменено другими операциями, которые вы ожидаете, нет причин ждать последовательного выполнения.

Таким образом, в этих случаях вы можете использовать очередь concurrent, как это делает Kingfisher, и установить флаг .barrier для любых операций, которые изменяют состояние данных, чтобы указать блоку ждать, пока все другие операции в очереди не будут завершены, прежде чем выполняться.

Я не могу говорить о конкретном обосновании использования Kingfisher очереди concurrent, но просто хотел отметить пример использования, когда вы можете предпочесть concurrent вместо serial для безопасности очереди GCD. Использование concurrent может обеспечить дополнительную гибкость, поскольку вы можете решить, должны ли операции выполняться одновременно или "последовательно" с флагом .barrier, в зависимости от поведения.

Описание этого шаблона см. в .barrier описании здесь: http://khanlou.com/2016/04/the-GCD-handbook/

person miweinst    schedule 30.03.2018