Diferença entre DispatchQueue.main.async e DispatchQueue.main.sync

99

Eu tenho usado DispatchQueue.main.asyncpor muito tempo para realizar operações relacionadas à IU.



O Swift fornece ambos DispatchQueue.main.asynce DispatchQueue.main.sync, e ambos são executados na fila principal.



Alguém pode me dizer a diferença entre eles? Quando devo usar cada um?



DispatchQueue.main.async {
    self.imageView.image = imageView
    self.lbltitle.text = ""

}

DispatchQueue.main.sync {
    self.imageView.image = imageView
    self.lbltitle.text = ""
}
Aman.Samghani
fonte

Respostas:

46

Quando você o usa, asyncele permite que a fila de chamadas continue sem esperar até que o bloco despachado seja executado. Ao contrário, syncfará com que a fila de chamadas pare e espere até que o trabalho que você despachou no bloco seja concluído. Portanto, syncestá sujeito a levar a bloqueios. Tente executar a DispatchQueue.main.syncpartir da fila principal e o aplicativo irá congelar porque a fila de chamadas irá esperar até que o bloco despachado termine, mas não será nem mesmo capaz de iniciar (porque a fila está parada e esperando)

Quando usar sync? Quando você precisa esperar algo feito em uma fila DIFERENTE e só então continuar trabalhando em sua fila atual

Exemplo de uso de sincronização:

Em uma fila serial, você pode usar synccomo um mutex para ter certeza de que apenas um thread é capaz de executar a parte protegida do código ao mesmo tempo.

Andrey Chernukha
fonte
Seria errado chamar DispatchQueue.main.syncde um thread em segundo plano?
Honey
@Honey Em geral, não, não há nada de errado com essa chamada (desde que a fila principal não faça nada pesado e demorado), mas na prática não consigo pensar em uma situação em que você realmente precise disso. Definitivamente deveria haver uma solução melhor
Andrey Chernukha
1
@Honey Uma dessas situações é atualizar um CollectionView de PHAssets da API PhotoKit, conforme demonstrado na documentação aqui: developer.apple.com/documentation/photokit/…
teacup
1
@teacup interessante. Só estou me perguntando como seria diferente se ligássemos para asynclá. Quero dizer, uma vez que não há mais nada no tópico depois disso, não faz diferença. Se fosse DispatchQueue.main.sync {block1}; DispatchQueue.main.sync {block2};, teria feito sentido. Mas quando não há outro bloqueio, não consigo pensar na vantagem de usar DispatchQueue.main.sync {Oneblock}novamente DispatchQueue.main.async {Oneblock}. Para ambos, eles obterão a prioridade / imediatismo mainQueue e nada os interromperá.
Honey,
3
@Honey "já que não há mais nada no tópico depois" não é verdade quando você está no tópico principal, que é responsável por lidar com todas as interações do usuário com o aplicativo. Assim, por exemplo, um usuário pode excluir outra foto antes que photoLibraryDidChange retorne com uma fonte de dados atualizada causando um erro fatal de inconsistência.
xícara de chá
160

Por que simultaneidade?

Assim que você adiciona tarefas pesadas ao seu aplicativo, como o carregamento de dados, ele retarda o trabalho da IU ou até o congela. A simultaneidade permite que você execute 2 ou mais tarefas “simultaneamente”. A desvantagem dessa abordagem é a segurança do thread, que nem sempre é tão fácil de controlar. Fe quando diferentes tarefas desejam acessar os mesmos recursos, como tentar alterar a mesma variável em diferentes threads ou acessar os recursos já bloqueados por diferentes threads.

Existem algumas abstrações das quais precisamos estar cientes.

  • Filas
  • Desempenho de tarefa síncrona / assíncrona.
  • Prioridades.
  • Problemas comuns.

Filas

Deve ser serial ou simultâneo . Bem como global ou privado ao mesmo tempo.

Com filas seriais, as tarefas serão concluídas uma a uma, enquanto com filas simultâneas, as tarefas serão realizadas simultaneamente e serão concluídas em agendamentos inesperados. O mesmo grupo de tarefas levará muito mais tempo em uma fila serial do que em uma fila simultânea.

Você pode criar suas próprias filas privadas ( seriais ou simultâneas ) ou usar filas globais (sistema) já disponíveis . A fila principal é a única fila serial de todas as filas globais .

É altamente recomendável não realizar tarefas pesadas que não sejam relacionadas ao trabalho da IU na fila principal (por exemplo, carregar dados da rede), mas sim realizá-las nas outras filas para manter a IU descongelada e responsiva às ações do usuário. Se permitirmos que a IU seja alterada nas outras filas, as alterações podem ser feitas em uma programação e velocidade diferentes e inesperadas. Alguns elementos da IU podem ser desenhados antes ou depois de serem necessários. Isso pode travar a IU. Também precisamos ter em mente que, como as filas globais são filas do sistema , algumas outras tarefas podem ser executadas pelo sistema nelas.

Qualidade de serviço / prioridade

As filas também têm diferentes qos (Quality of Service), que define a prioridade de execução da tarefa (da mais alta para a mais baixa aqui):
.userInteractive - fila principal
.userInitiated - para as tarefas iniciadas pelo usuário nas quais o usuário espera por alguma resposta
.utilidade - para as tarefas que leva algum tempo e não requer resposta imediata, por exemplo, trabalhar com dados
.background - para as tarefas que não estão relacionadas com a parte visual e que não são restritas para o tempo de conclusão).

Também existe uma fila

.default que não transfere as informações de qos . Se não foi possível detectar o qos oqos será usado entre .userInitiated e .utility .

As tarefas podem ser realizadas de forma síncrona ou assíncrona .

  • A função síncrona retorna o controle para a fila atual somente após a conclusão da tarefa. Ele bloqueia a fila e espera até que a tarefa seja concluída.

  • A função assíncrona retorna o controle para a fila atual logo após a tarefa ter sido enviada para ser executada na fila diferente. Não espera até que a tarefa seja concluída. Não bloqueia a fila.

Problemas comuns.

Os erros mais comuns que os programadores cometem ao projetar os aplicativos simultâneos são os seguintes:

  • Condição de corrida - causada quando o aplicativo funciona depende da ordem de execução das partes do código.
  • Inversão de prioridade - quando as tarefas de prioridade mais alta aguardam que as tarefas de prioridade menor sejam concluídas devido ao bloqueio de alguns recursos
  • Deadlock - quando algumas filas têm espera infinita pelas fontes (variáveis, dados etc.) já bloqueadas por alguma dessas filas.

NUNCA chame a função de sincronização na fila principal .
Se você chamar a função de sincronização na fila principal, ela bloqueará a fila, assim como a fila ficará esperando a tarefa ser concluída, mas a tarefa nunca será concluída, pois não será possível nem mesmo iniciar devido à fila já bloqueado. É chamado de impasse .

Quando usar a sincronização? Quando precisamos esperar até que a tarefa seja concluída. Veja quando estamos nos certificando de que alguma função / método não seja duplamente chamada. Sim, temos sincronização e estamos tentando evitar que seja duplamente chamado até que esteja completamente concluído. Aqui está um código para essa preocupação:
Como descobrir o que causou o relatório de falha de erro no dispositivo IOS?

Alexandre
fonte
3
Não acho que "NUNCA chame a função de sincronização na fila principal" esteja certo. Há casos em que você chamaria a sincronização no encadeamento principal, por exemplo, quando você tem um contador global que precisa que cada objeto use e aumenta o: dispatchQueue.sync {count + = 1; self.orderId = count}
Elisha Sterngold
6
Classe QOS - .userInteractive NÃO é a fila principal.
Kunal Shah
1
Seria errado chamar DispatchQueue.main.syncde um thread em segundo plano?
Honey
1
@Honey, não, não é errado chamar isso, mas pela minha experiência, você se pegaria chamando mais de DispatchQueue.main.async além de sync.
James Kim
2
Não seria mais preciso dizer que você nunca deve chamar a função sync () na fila atual? Não é errado chamar sync () na fila principal se você estiver em outra fila, se entendi corretamente.
ykay
0

syncou os asyncmétodos não têm efeito na fila em que são chamados.

syncbloqueará o encadeamento a partir do qual é chamado e não a fila na qual é chamado. É a propriedade DispatchQueueque decide se o DispatchQueueaguardará pela execução da tarefa (fila serial) ou se poderá executar a próxima tarefa antes que a tarefa atual seja concluída (fila simultânea).

Portanto, mesmo quando DispatchQueue.main.asyncé uma chamada assíncrona, uma operação pesada adicionada a ela pode congelar a IU, pois suas operações são executadas em série no encadeamento principal. Se esse método for chamado a partir do thread em segundo plano, o controle retornará a esse thread instantaneamente, mesmo quando a IU parecer estar congelada. Isso ocorre porque a asyncchamada é feita emDispatchQueue.main

Vipin
fonte
0

GCDpermite que você execute uma tarefa synchronouslyou asynchronously[Sobre] [Mais]

synchronous(bloquear e esperar) a função retorna um controle quando a tarefa será concluída

asynchronous(despachar e prosseguir) retorna um controle imediatamente, despachando a tarefa para iniciar em uma fila apropriada, mas não esperando que ela seja concluída.

yoAlex5
fonte