Ваш второй пример будет запускать их последовательно. Он выполняет единую рассылку, выполняя их одну за другой. В первом примере они будут запускаться параллельно, отправляя каждый в другой рабочий поток. К сожалению, ни одна из них не использует группу отправки правильно.
Что касается группы отправки, вы должны определить ее перед l oop и enter
, прежде чем звонить по номеру async
. Но ручной вызов enter
и leave
необходим только в том случае, если вы вызываете асинхронный процесс из вызова async
. Но учитывая, что decompress
, вероятно, является синхронным процессом, вы можете просто указать группу в async
, и он обо всем позаботится за вас:
let group = DispatchGroup()
for file in compressedFiles {
DispatchQueue.global(qos: .userInteractive).async(group: group) {
file.decompress()
}
}
group.notify(queue: .main) {
// all done
}
Но вместо того, чтобы беспокоиться о logi c группы диспетчеризации, в параллельном примере есть более глубокая проблема. В частности, он страдает от взрыва потока, когда он может превышать количество доступных ядер на вашем процессоре. Хуже того, если у вас было много файлов для распаковки, вы даже можете превысить ограниченное количество рабочих потоков, которые GCD имеет в своем пуле. И когда это произойдет, это может предотвратить запуск чего-либо еще на GCD для этого QoS. Вместо этого вы хотите запускать его параллельно, но вы хотите ограничить его разумной степенью параллелизма, сохраняя при этом параллелизм, чтобы избежать истощения ресурсов для других задач.
Если вы хотите, чтобы он работал параллельно , но и избежать взрыва резьбы, часто можно достичь concurrentPerform
. Это обеспечивает максимальный параллелизм, поддерживаемый ЦП, но предотвращает проблемы, которые могут возникнуть в результате взрыва потока:
DispatchQueue.global(qos: .userInitiated).async {
DispatchQueue.concurrentPerform(iterations: compressedFiles.count) { index in
compressedFiles[index].decompress()
}
DispatchQueue.main.async {
// all done
}
}
Это ограничит параллелизм до максимума, разрешенного ядрами вашего устройства. Это также устраняет необходимость в диспетчерской группе.
В качестве альтернативы, если вы хотите наслаждаться параллелизмом, но с более низкой степенью параллелизма (например, чтобы оставить некоторые ядра доступными для других задач, чтобы минимизировать пиковое использование памяти и т. Д. c.), Вы можете использовать очереди операций и maxConcurrentOperationCount
:
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 4 // a max of 4 decompress tasks at a time
let completion = BlockOperation {
// all done
}
for file in compressedFiles {
let operation = BlockOperation {
file.decompress()
}
completion.addDependency(operation)
queue.addOperation(operation)
}
OperationQueue.main.addOperation(completion)
Или, как указывает Мэтт, в iOS 13 (или macOS 10.15) и более поздних версиях вы можете сделать:
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 4
for file in compressedFiles {
queue.addOperation {
file.decompress()
}
}
queue.addBarrierBlock {
DispatchQueue.main.async {
// all done
}
}