Редактировать:
Я бы хотел убедиться, что оболочка OpenGL действительно поточно-ориентированная.Каждый рендер, имеющий свою собственную последовательную очередь, может не помочь, если несколько рендеров делают вызовы OpenGL одновременно.Возможно, версия DispatchQueue.concurrentPerform
работает, потому что она работает только последовательно.
Оригинальный ответ:
Я подозреваю, что сбои OpenGL связаны с ограничениями памяти.Когда вы отправляете много задач в параллельную очередь, GCD не делает ничего умного, чтобы ограничить число запускаемых задач.Если куча запущенных задач блокируется с помощью ввода-вывода, он может просто запустить все больше и больше задач, прежде чем любая из них завершится, сожрать все больше и больше памяти. Вот подробное описание Майка Эша о проблеме .
Я бы предположил, что DispatchQueue.concurrentPerform
работает, потому что у него есть какая-то дополнительная логика внутри, чтобы избежать порождения слишком большого количества потоков, хотя это плохо документировано, и здесь могут происходить вещи, специфичные для платформы.Я не уверен, почему эта функция вообще существует, если все, что она делает, отправляет в параллельную очередь.
Если вы хотите отправить большое количество элементов непосредственно в DispatchQueue
, особенно если эти элементыЕсли у вас есть не связанные с процессором компоненты, вам нужно добавить дополнительную логику, чтобы ограничить число запускаемых задач.Вот пример из Справочника Сороуса Ханлоу по GCD :
class LimitedWorker {
private let serialQueue = DispatchQueue(label: "com.khanlou.serial.queue")
private let concurrentQueue = DispatchQueue(label: "com.khanlou.concurrent.queue", attributes: .concurrent)
private let semaphore: DispatchSemaphore
init(limit: Int) {
semaphore = DispatchSemaphore(value: limit)
}
func enqueue(task: @escaping () -> ()) {
serialQueue.async(execute: {
self.semaphore.wait()
self.concurrentQueue.async(execute: {
task()
self.semaphore.signal()
})
})
}
}
Он использует семафор для ограничения числа одновременных задач, выполняемых в параллельной очереди, и использует последовательную очередь для подачиновые задачи в параллельной очереди.Вновь поставленные в очередь задачи блокируются на self.semaphore.wait()
, если максимальное число задач уже запланировано в параллельной очереди.
Вы можете использовать его следующим образом:
let group = DispatchGroup()
let q = LimitedWorker(limit: 10) // Experiment with this number
for (j, renderer) in renderers.enumerated() {
group.enter()
q.enqueue {
let batch = batches[j]
let _ = renderer.draw(data:batch)
group.leave()
}
}
group.wait()