При использовании GCD мы хотим подождать, пока два асинхронных блока не будут выполнены и выполнены, прежде чем перейти к следующим шагам выполнения. Каков наилучший способ сделать это?
Мы попробовали следующее, но это не сработало:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block1
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block2
});
// wait until both the block1 and block2 are done before start block3
// how to do that?
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block3
});
Ответы:
Используйте группы рассылки: см. Здесь пример «Ожидание групп задач, поставленных в очередь» в главе «Очереди рассылки» Руководства по программированию параллелизма Apple Developer Developer Library
Ваш пример может выглядеть примерно так:
и мог бы произвести вывод как это:
источник
dispatch_group_async
так же, какdispatch_async
с добавленным параметром группы. Поэтому, если вы используете разные очереди для block1 и block2 или планируете их в одной и той же параллельной очереди, они могут работать одновременно; если вы запланируете их в одной последовательной очереди, они будут работать последовательно. Это ничем не отличается от планирования блоков без групп.Расширяя ответ Jörn Eyrich (добавьте его ответ, если вы подтвердите этот ответ), если у вас нет контроля над
dispatch_async
вызовами для ваших блоков, как это может быть в случае асинхронных блоков завершения, вы можете использовать группы GCD, используяdispatch_group_enter
иdispatch_group_leave
напрямую.В этом примере мы притворяемся
computeInBackground
, что это то, что мы не можем изменить (представьте, что это обратный вызов делегата, NSURLConnection completeHandler или что-то еще), и поэтому у нас нет доступа к вызовам диспетчеризации.В этом примере computeInBackground: завершение: реализовано как:
Вывод (с метками времени из прогона):
источник
dispatch_queue_notify
, вероятно, он лучше (если время блокировки не гарантировано будет коротким).С помощью Swift 5.1 Grand Central Dispatch предлагает множество способов решения вашей проблемы. В соответствии с вашими потребностями вы можете выбрать один из семи шаблонов, показанных в следующих фрагментах игровой площадки.
# 1. Используя
DispatchGroup
,DispatchGroup
'snotify(qos:flags:queue:execute:)
иDispatchQueue
' sasync(group:qos:flags:execute:)
В Руководстве по программированию параллелизма разработчиков Apple говорится о
DispatchGroup
:# 2. Использование
DispatchGroup
,DispatchGroup
swait()
,DispatchGroup
senter()
иDispatchGroup
sleave()
Обратите внимание , что вы можете также смешать
DispatchGroup
wait()
сDispatchQueue
async(group:qos:flags:execute:)
или смешиватьDispatchGroup
enter()
иDispatchGroup
leave()
сDispatchGroup
notify(qos:flags:queue:execute:)
.# 3. Использование и «S
DispatchWorkItemFlags
barrier
DispatchQueue
async(group:qos:flags:execute:)
Учебное пособие по Grand Central для Swift 4: статья 1/2 от Raywenderlich.com дает определение барьеров :
Использование:
# 4. Используя
DispatchWorkItem
,DispatchWorkItemFlags
'sbarrier
иDispatchQueue
' sasync(execute:)
# 5. Используя
DispatchSemaphore
,DispatchSemaphore
'swait()
иDispatchSemaphore
' ssignal()
Соруш Ханлоу написал следующие строки в блоге GCD Handbook :
Справочник по Apple Developer API также дает следующее обсуждение
DispatchSemaphore
init(value:)
инициализатора:Использование:
# 6. Использование
OperationQueue
иOperation
«SaddDependency(_:)
Справочник по Apple Developer API гласит
OperationQueue
:Использование:
# 7. Использование
OperationQueue
иOperationQueue
saddBarrierBlock(_:)
(требуется iOS 13)источник
Другой альтернативой GCD является барьер:
Просто создайте параллельную очередь, отправьте два блока, а затем отправьте последний блок с барьером, который заставит его ждать окончания двух других.
источник
sleep()
! Я добавил этиsleep()
призывы только по педагогическим соображениям, чтобы блоки работали достаточно долго, чтобы вы могли видеть, что они работают одновременно. В этом тривиальном примере, при отсутствииsleep()
, эти два блока могут выполняться так быстро, что отправленный блок может начинаться и заканчиваться до того, как вы сможете эмпирически наблюдать за одновременным выполнением. Но неsleep()
в своем собственном коде.Я знаю, что вы спрашивали о GCD, но, если хотите,
NSOperationQueue
также обрабатывает такие вещи действительно изящно, например:источник
NSOperation
подкласс, который является параллельным и устанавливаетсяisFinished
после завершения асинхронного процесса. Тогда зависимости работают нормально.dispatch_semaphore_wait
это не происходит в основной очереди и пока ваши сигналы и ожидания сбалансированы). Пока вы не блокируете основную очередь, семафорный подход хорош, если вам не нужна гибкость операций (например, возможность отменить их, возможность контролировать степень параллелизма и т. Д.).maxConcurrentOperationCount
в1
. Вы также можете установить приоритет операций, какqualityOfService
иqueuePriority
, но они оказывают гораздо более тонкое влияние на приоритет задачи, чем зависимости и / или степень параллелизма очереди.Ответы выше все классные, но все они пропустили одну вещь. группа выполняет задачи (блоки) в потоке, куда она вошла, когда вы используете
dispatch_group_enter
/dispatch_group_leave
.это выполняется в созданной параллельной очереди
demoQueue
. Если я не создаю какую-либо очередь, она запускается в основном потоке .и есть третий способ сделать задачи выполненными в другом потоке:
Конечно, как уже упоминалось, вы можете использовать,
dispatch_group_async
чтобы получить то, что вы хотите.источник
Первый ответ, по сути, правильный, но если вы хотите, чтобы самый простой способ достиг желаемого результата, вот отдельный пример кода, демонстрирующий, как это сделать с семафором (так же работают диспетчерские группы за кулисами, JFYI) :
источник
dispatch_semaphore_wait
. У вас есть два сигнала, поэтому вам нужно два ожидания. Таким образом, ваш блок «завершения» начнется, как только первый блок подаст сигнал семафору, но до того, как закончится другой блок; 2. Учитывая, что это вопрос iOS, я бы не рекомендовал использоватьdispatch_main
.dispatch_semaphore_wait
Разблокируют , как только любой изdispatch_semaphore_signal
методов называются. Причина, по которой это может сработать, заключается в том, чтоprintf
блоки for «one» и «two» возникают немедленно, аprintf
for «finally» происходит после ожидания - таким образом, после того, как блок один проспал 2 секунды. Если вы поместите printf послеsleep
вызовов, вы получите вывод для 'one', затем через 2 секунды для 'finally', затем через 2 секунды для 'two'.Принятый ответ в кратчайшие сроки:
источник
Swift 4.2 пример:
источник
group.leave()
вызвал авариюНельзя сказать, что другие ответы не подходят для определенных обстоятельств, но это один фрагмент, который я всегда использую в Google:
источник