Différence entre DispatchQueue.main.async et DispatchQueue.main.sync


100

J'utilise depuis DispatchQueue.main.asynclongtemps pour effectuer des opérations liées à l'interface utilisateur.



Swift fournit à la fois DispatchQueue.main.asyncet DispatchQueue.main.sync, et les deux sont exécutés sur la file d'attente principale.



Quelqu'un peut-il me dire la différence entre eux? Quand devrais-je utiliser chacun?



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

}

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

Réponses:


47

Lorsque vous l'utilisez async, la file d'attente des appels se déplace sans attendre que le bloc distribué soit exécuté. Au contraire sync, la file d'attente des appels s'arrêtera et attendra que le travail que vous avez distribué dans le bloc soit terminé. syncEst donc sujet à conduire à des blocages. Essayez de courir à DispatchQueue.main.syncpartir de la file d'attente principale et l'application se bloquera car la file d'attente des appels attendra la fin du bloc distribué, mais elle ne pourra même pas démarrer (car la file d'attente est arrêtée et en attente)

Quand l'utiliser sync? Lorsque vous devez attendre que quelque chose soit fait sur une file d'attente DIFFÉRENTE et seulement ensuite continuer à travailler sur votre file d'attente actuelle

Exemple d'utilisation de la synchronisation:

Sur une file d'attente série, vous pouvez l'utiliser synccomme mutex afin de vous assurer qu'un seul thread est capable d'exécuter le morceau de code protégé en même temps.


Serait-il faux d'appeler à DispatchQueue.main.syncpartir d'un fil d'arrière-plan?
Honey

@Honey En général non, il n'y a rien de mal avec un tel appel (tant que la file d'attente principale ne fait rien de lourd et de temps), mais en pratique, je ne peux pas penser à une situation où vous en avez vraiment besoin. Il devrait certainement y avoir une meilleure solution
Andrey Chernukha

1
@Honey Une telle situation consiste à mettre à jour une CollectionView de PHAssets à partir de l'API PhotoKit, comme illustré dans la documentation ici: developer.apple.com/documentation/photokit/…
teacup

1
@teacup intéressant. Je me demande simplement en quoi ce serait différent si nous appelions asynclà-bas? Je veux dire, puisqu'il n'y a rien d'autre sur le fil après, cela ne fait aucune différence. Si c'était le cas, cela DispatchQueue.main.sync {block1}; DispatchQueue.main.sync {block2};aurait eu du sens. Mais quand il n'y a pas d'autre bloc, je ne peux pas penser à l'avantage d'utiliser DispatchQueue.main.sync {Oneblock}over DispatchQueue.main.async {Oneblock}. Pour les deux, ils obtiendront la priorité / l'immédiateté mainQueue et rien ne les interrompra.
Honey

3
@Honey "car il n'y a rien d'autre sur le thread après" n'est pas vrai lorsque vous êtes sur le thread principal, qui est chargé de gérer toutes les interactions des utilisateurs avec l'application. Ainsi, par exemple, un utilisateur peut supprimer une autre photo avant le retour de photoLibraryDidChange avec une source de données mise à jour provoquant une erreur d'incohérence fatale.
tasse de thé du

161

Pourquoi la concurrence?

Dès que vous ajoutez des tâches lourdes à votre application, comme le chargement de données, cela ralentit le travail de votre interface utilisateur ou même la gèle. La simultanéité vous permet d'effectuer 2 tâches ou plus «simultanément». L'inconvénient de cette approche est que la sécurité des fils n'est pas toujours aussi facile à contrôler. Fe lorsque différentes tâches veulent accéder aux mêmes ressources, comme essayer de modifier la même variable sur un autre thread ou accéder aux ressources déjà bloquées par les différents threads.

Il y a quelques abstractions dont nous devons être conscients.

  • Files d'attente.
  • Performances des tâches synchrones / asynchrones.
  • Priorités.
  • Problèmes courants.

Files d'attente

Doit être en série ou simultané . Ainsi que global ou privé en même temps.

Avec les files d'attente en série, les tâches seront terminées une par une, tandis qu'avec les files d'attente simultanées, les tâches seront exécutées simultanément et seront terminées selon des horaires inattendus. Le même groupe de tâches prendra beaucoup plus de temps sur une file d'attente série par rapport à une file d'attente simultanée.

Vous pouvez créer vos propres files d'attente privées (en série ou simultanées ) ou utiliser des files d'attente globales (système) déjà disponibles . La file d'attente principale est la seule file d'attente série parmi toutes les files d'attente globales .

Il est fortement recommandé de ne pas effectuer de tâches lourdes qui ne font pas référence au travail de l'interface utilisateur sur la file d'attente principale (par exemple, le chargement de données à partir du réseau), mais plutôt de les effectuer sur les autres files d'attente pour que l'interface utilisateur reste dégelée et réagisse aux actions de l'utilisateur. Si nous laissons l'interface utilisateur être modifiée sur les autres files d'attente, les modifications peuvent être effectuées selon un horaire et une vitesse différents et inattendus. Certains éléments de l'interface utilisateur peuvent être dessinés avant ou après leur utilisation. Cela peut planter l'interface utilisateur. Nous devons également garder à l'esprit que, puisque les files d'attente globales sont des files d'attente système , d'autres tâches peuvent être exécutées par le système.

Qualité de service / priorité

Les files d'attente ont également des qos (qualité de service) différents qui définissent la priorité d' exécution de la tâche (du plus élevé au plus bas ici):
.userInteractive - file d'attente principale
.userInitiated - pour les tâches lancées par l'utilisateur sur lesquelles l'utilisateur attend une réponse
.utility - pour les tâches qui prend un certain temps et ne nécessite pas de réponse immédiate, par exemple travailler avec des données
.background - pour les tâches qui ne sont pas liées à la partie visuelle et qui ne sont pas strictes pour le temps de réalisation).

Il existe également

une file d'attente .default qui ne transfère pas les informations qos . S'il n'était pas possible de détecter le qos, leqos sera utilisé entre .userInitiated et .utility .

Les tâches peuvent être effectuées de manière synchrone ou asynchrone .

  • La fonction synchrone renvoie le contrôle de la file d'attente actuelle uniquement une fois la tâche terminée. Il bloque la file d'attente et attend que la tâche soit terminée.

  • La fonction asynchrone retourne le contrôle à la file d'attente actuelle juste après que la tâche a été envoyée pour être effectuée sur la file d'attente différente. Il n'attend pas que la tâche soit terminée. Cela ne bloque pas la file d'attente.

Problèmes courants.

Les erreurs les plus courantes commises par les programmeurs lors de la projection des applications simultanées sont les suivantes:

  • Condition de concurrence - causée lorsque le travail de l'application dépend de l'ordre d'exécution des parties de code.
  • Inversion de priorité - lorsque les tâches de priorité plus élevée attendent que les tâches de priorité plus petites soient terminées en raison du blocage de certaines ressources
  • Deadlock - lorsque quelques files d'attente ont une attente infinie pour les sources (variables, données, etc.) déjà bloquées par certaines de ces files d'attente.

N'appelez JAMAIS la fonction de synchronisation sur la file d'attente principale .
Si vous appelez la fonction de synchronisation sur la file d'attente principale, elle bloquera la file d'attente et la file d'attente attendra que la tâche soit terminée, mais la tâche ne sera jamais terminée car elle ne pourra même pas démarrer car la file d'attente est déjà bloqué. Cela s'appelle une impasse .

Quand utiliser la synchronisation? Lorsque nous devons attendre que la tâche soit terminée. Fe lorsque nous nous assurons qu'une fonction / méthode n'est pas appelée en double. Fe nous avons la synchronisation et essayons de l'empêcher d'être doublé jusqu'à ce qu'elle soit complètement terminée. Voici un code pour ce problème:
Comment savoir ce qui a provoqué le rapport d'erreur sur le périphérique IOS?


3
Je ne pense pas que "N'appelez JAMAIS la fonction de synchronisation sur la file d'attente principale" est correct. Il y a des cas où vous appelleriez la synchronisation dans le thread principal, par exemple lorsque vous avez un compteur global dont vous avez besoin pour chaque objet à utiliser et augmentez le: dispatchQueue.sync {count + = 1; self.orderId = count}
Elisha Sterngold

6
Classe QOS - .userInteractive n'est PAS la file d'attente principale.
Kunal Shah

1
Serait-il faux d'appeler à DispatchQueue.main.syncpartir d'un fil d'arrière-plan?
Honey

1
@Honey, non ce n'est pas faux d'appeler cela, mais d'après mon expérience, vous vous retrouveriez à appeler davantage DispatchQueue.main.async autre que sync.
James Kim

2
Ne serait-il pas plus précis de dire que vous ne devriez jamais appeler la fonction sync () sur la file d'attente actuelle? Il n'est pas faux d'appeler sync () sur la file d'attente principale si vous êtes dans une autre file d'attente, si je comprends bien.
ykay

0

syncou les asyncméthodes n'ont aucun effet sur la file d'attente sur laquelle elles sont appelées.

syncbloquera le thread à partir duquel il est appelé et non la file d' attente sur laquelle il est appelé. C'est la propriété DispatchQueuequi décide si le DispatchQueueva attendre l'exécution de la tâche (file d'attente série) ou peut exécuter la tâche suivante avant que la tâche en cours ne soit terminée (file d'attente simultanée).

Ainsi, même en cas DispatchQueue.main.asyncd'appel asynchrone, une opération lourde ajoutée peut geler l'interface utilisateur lorsque ses opérations sont exécutées en série sur le thread principal. Si cette méthode est appelée à partir du thread d'arrière-plan, le contrôle retournera instantanément à ce thread même lorsque l'interface utilisateur semble figée. C'est parce que l' asyncappel est passéDispatchQueue.main


0

GCDvous permet d'exécuter une tâche synchronouslyou asynchronously[À propos] [Plus]

synchronous(bloquer et attendre) la fonction retourne un contrôle lorsque la tâche sera terminée

asynchronous(expédier et procéder) renvoie un contrôle immédiatement, distribuant la tâche pour qu'elle démarre dans une file d'attente appropriée mais sans attendre qu'elle se termine.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.