Ao usar o GCD, queremos esperar até que dois blocos assíncronos sejam executados e concluídos antes de passar para as próximas etapas de execução. Qual o melhor jeito pra fazer isso?
Tentamos o seguinte, mas parece não funcionar:
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
});
Respostas:
Use grupos de despacho: veja aqui um exemplo, "Aguardando grupos de tarefas na fila" no capítulo "Filas de despacho" do Guia de programação de simultaneidade da biblioteca de desenvolvedores iOS da Apple
Seu exemplo pode ser algo como isto:
e poderia produzir uma saída como esta:
fonte
dispatch_group_async
é comodispatch_async
com um parâmetro de grupo adicionado. Portanto, se você usar filas diferentes para o bloco1 e o bloco2 ou planejá-las na mesma fila simultânea, elas poderão ser executadas simultaneamente; se você agendá-los na mesma fila serial, eles serão executados em série. Não é diferente de agendar os blocos sem grupos.Expandindo a resposta de Jörn Eyrich (upvote a resposta dele se você aprovou esta), se você não tiver controle sobre as
dispatch_async
chamadas para seus blocos, como pode ser o caso dos blocos de conclusão assíncrona, você pode usar os grupos GCD usandodispatch_group_enter
edispatch_group_leave
diretamente.Neste exemplo, estamos fingindo que
computeInBackground
é algo que não podemos mudar (imagine que seja um retorno de chamada delegado, NSURLConnection completeHandler ou qualquer outra coisa) e, portanto, não temos acesso às chamadas de despacho.Neste exemplo, computeInBackground: conclusão: é implementado como:
Saída (com registros de data e hora de uma execução):
fonte
dispatch_queue_notify
melhor é provavelmente (a menos que o tempo de bloqueio seja curto).Com o Swift 5.1, o Grand Central Dispatch oferece várias maneiras de resolver seu problema. De acordo com suas necessidades, você pode escolher um dos sete padrões mostrados nos seguintes snippets do Playground.
# 1 Usando
DispatchGroup
,DispatchGroup
'snotify(qos:flags:queue:execute:)
eDispatchQueue
' sasync(group:qos:flags:execute:)
O Guia de Programação de Concorrência para Desenvolvedor da Apple declara sobre
DispatchGroup
:# 2 Usando
DispatchGroup
,DispatchGroup
'swait()
,DispatchGroup
senter()
eDispatchGroup
'sleave()
Note que você também pode misturar
DispatchGroup
wait()
comDispatchQueue
async(group:qos:flags:execute:)
ou misturarDispatchGroup
enter()
eDispatchGroup
leave()
comDispatchGroup
notify(qos:flags:queue:execute:)
.# 3 Usando e
DispatchWorkItemFlags
barrier
DispatchQueue
'sasync(group:qos:flags:execute:)
Tutorial de Despacho da Grand Central para Swift 4: Parte 1/2 artigo da de Raywenderlich.com fornece uma definição para barreiras :
Uso:
# 4 Usando
DispatchWorkItem
,DispatchWorkItemFlags
'sbarrier
eDispatchQueue
' sasync(execute:)
# 5 Usando
DispatchSemaphore
,DispatchSemaphore
'swait()
eDispatchSemaphore
' ssignal()
Soroush Khanlou escreveu as seguintes linhas na postagem do blog The GCD Handbook :
O Apple Developer API Reference também fornece a seguinte discussão para
DispatchSemaphore
init(value:)
inicializador:Uso:
# 6 Usando
OperationQueue
eOperation
'saddDependency(_:)
A Referência da API do desenvolvedor da Apple declara sobre
OperationQueue
:Uso:
# 7 Usando
OperationQueue
eOperationQueue
'saddBarrierBlock(_:)
(requer iOS 13)fonte
Outra alternativa da GCD é uma barreira:
Basta criar uma fila simultânea, despachar seus dois blocos e, em seguida, despachar o bloco final com barreira, o que fará com que espere os outros dois terminarem.
fonte
sleep()
! Eu apenas adicionei essassleep()
chamadas por razões pedagógicas, para fazer com que os blocos rodem o suficiente para que você possa ver que eles são executados simultaneamente. Neste exemplo trivial, na ausência desleep()
, esses dois blocos podem ser executados tão rapidamente que o bloco despachado pode começar e terminar antes que você tenha a chance de observar empiricamente a execução simultânea. Mas nãosleep()
no seu próprio código.Eu sei que você perguntou sobre a GCD, mas se você quiser,
NSOperationQueue
também lida com esse tipo de coisa com muita elegância, por exemplo:fonte
NSOperation
subclasse que é simultânea e definidaisFinished
quando o processo assíncrono é concluído. Então as dependências funcionam bem.dispatch_semaphore_wait
não ocorra na fila principal e desde que seus sinais e esperas estejam equilibrados). Desde que você não bloqueie a fila principal, uma abordagem de semáforo é boa, se você não precisar da flexibilidade das operações (por exemplo, ter a capacidade de cancelá-las, capacidade de controlar o grau de simultaneidade, etc.).maxConcurrentOperationCount
como1
. Você também pode definir a prioridade das operações, tanto aqualityOfService
como aqueuePriority
, mas elas têm um impacto muito mais sutil na prioridade da tarefa do que as dependências e / ou o grau de simultaneidade da fila.As respostas acima são legais, mas todas elas perderam uma coisa. O grupo executa tarefas (blocos) no encadeamento em que foi inserido quando você usa
dispatch_group_enter
/dispatch_group_leave
.isso é executado na fila simultânea criada
demoQueue
. Se eu não criar nenhuma fila, ela será executada no thread principal .e há uma terceira maneira de fazer tarefas executadas em outro segmento:
Obviamente, como mencionado, você pode usar
dispatch_group_async
para obter o que deseja.fonte
A primeira resposta está essencialmente correta, mas se você deseja a maneira mais simples de obter o resultado desejado, aqui está um exemplo de código independente demonstrando como fazê-lo com um semáforo (que também é como os grupos de despacho funcionam nos bastidores, JFYI) :
fonte
dispatch_semaphore_wait
. Você tem dois sinais, então precisa de duas esperas. Como é, seu bloco de "conclusão" começará assim que o primeiro bloco sinalizar o semáforo, mas antes que o outro bloco termine; 2. Como essa era uma pergunta do iOS, eu desencorajaria o uso dedispatch_main
.dispatch_semaphore_wait
será desbloqueado assim que um dosdispatch_semaphore_signal
métodos for chamado. A razão pela qual isso parece funcionar é que osprintf
blocos 'um' e 'dois' ocorrem imediatamente, eprintf
o 'finalmente' ocorre após uma espera - assim, depois que o bloco dorme por 2 segundos. Se você colocar o printf após assleep
chamadas, obterá a saída para 'um', depois 2 segundos para 'finalmente' e, em seguida, 2 segundos depois para 'dois'.Resposta aceita rapidamente:
fonte
Exemplo do Swift 4.2:
fonte
group.leave()
causou um acidentePara não dizer que outras respostas não são ótimas para determinadas circunstâncias, mas este é um trecho que eu sempre uso do Google:
fonte