177 votes

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

J'ai utilisé DispatchQueue.main.async depuis longtemps pour effectuer des opérations liées à l'interface utilisateur.

Swift fournit à la fois DispatchQueue.main.async y DispatchQueue.main.sync et toutes deux sont exécutées sur la file d'attente principale.

Quelqu'un peut-il me dire quelle est la différence entre les deux ? Quand dois-je les utiliser ?

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

}

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

239voto

Alexander Points 1470

Pourquoi la concussion ?

Dès que vous ajoutez des tâches lourdes à votre application, comme le chargement de données, votre interface utilisateur est ralentie, voire figée. La concussion vous permet d'exécuter 2 ou plusieurs tâches "simultanément". L'inconvénient de cette approche est la sécurité des threads qui n'est pas toujours aussi facile à contrôler. Par exemple, lorsque différentes tâches veulent accéder aux mêmes ressources, comme essayer de modifier la même variable sur différents threads ou accéder aux ressources déjà bloquées par les différents threads.

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

  • Les files d'attente.
  • Performance synchrone/asynchrone des tâches.
  • Priorités.
  • Problèmes courants.

Files d'attente

Doit être série o concurrentes . Ainsi que mondial o 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 imprévus. Le même groupe de tâches prendra beaucoup plus de temps dans une file d'attente sérielle que dans une file d'attente concurrente.

Vous pouvez créer votre propre files d'attente privées (les deux série o concurrentes ) ou utiliser des files d'attente globales (système) . Le site file principale est le seul file d'attente en série parmi tous les files d'attente globales .

Il est fortement recommandé de ne pas effectuer de tâches lourdes qui ne sont pas référencées comme des travaux d'IU sur la file principale (par exemple, le chargement de données depuis le réseau), mais plutôt de les faire sur les autres files d'attente afin de garder l'interface utilisateur dégelée et réactive aux actions de l'utilisateur. Si nous laissons l'interface utilisateur être modifiée sur les autres files d'attente, les changements peuvent être effectués selon un calendrier et une vitesse différents et inattendus. Certains éléments de l'interface peuvent être dessinés avant ou après qu'ils soient nécessaires. Cela peut faire planter l'interface utilisateur. Nous devons également garder à l'esprit que, puisque l'application files d'attente globales sont files d'attente du système il y a d'autres tâches que le système peut exécuter sur eux.

Qualité du service / Priorité

Les files d'attente ont également différentes qos (Qualité de service) qui définit la tâche exécutée priorité (du plus élevé au plus bas ici) :
.userInteractive - file principale
.userInitiated - pour les tâches initiées par l'utilisateur et pour lesquelles l'utilisateur attend une réponse.
.utilitaire - pour les tâches qui prennent un certain temps et ne nécessitent 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

.par défaut qui ne transfère pas la qos informations. S'il n'était pas possible de détecter le qos le site qos sera utilisé entre .userInitiated y .utilitaire .

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

  • Synchrone renvoie le contrôle à la file d'attente actuelle uniquement lorsque la tâche est terminée. Elle bloque la file d'attente et attend que la tâche soit terminée.

  • Asynchrone retourne le contrôle à la file d'attente actuelle juste après que la tâche ait été envoyée pour être exécutée dans une autre file d'attente. Elle n'attend pas que la tâche soit terminée. Elle ne bloque pas la file d'attente.

Problèmes communs.

Les erreurs les plus courantes commises par les programmeurs lors de la conception d'applications concurrentes sont les suivantes :

  • Condition de course - causés lors du fonctionnement de l'application dépendent de l'ordre d'exécution des parties du code.
  • Inversion de priorité - lorsque les tâches de priorité supérieure attendent que les tâches de priorité inférieure soient terminées parce que certaines ressources sont bloquées.
  • Impasse - 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.

Ne JAMAIS appeler la fonction de synchronisation sur la file d'attente principale. .
Si vous appelez la fonction de synchronisation sur la file d'attente principale, cela bloquera la file d'attente ainsi que la file d'attente qui attendra que la tâche soit terminée, mais la tâche ne sera jamais terminée puisqu'elle ne pourra même pas commencer parce que la file d'attente est déjà bloquée. Cela s'appelle impasse .

Quand utiliser la synchronisation ? Lorsque nous devons attendre que la tâche soit terminée. Par exemple, lorsque nous nous assurons qu'une fonction/méthode n'est pas appelée deux fois. Par exemple, nous avons la synchronisation et nous essayons d'empêcher qu'elle soit appelée deux fois avant qu'elle ne soit complètement terminée. Voici un peu de code pour cette préoccupation :
Comment trouver ce qui a causé le rapport d'erreur de crash sur un appareil IOS ?

0 votes

Bonjour Alexander, pouvez-vous confirmer si la QoS .userInteractive est distribuée sur le thread principal ? Parce que j'ai essayé de placer un point d'arrêt à l'intérieur du bloc asynchrone et j'ai regardé le panneau des threads actifs du Debug Navigator. Lorsque DispatchQueue.global(qos : .userInteractive).async{} est appelé depuis le thread principal, il s'affiche avec un nom différent de celui du thread principal

0 votes

Bonjour, Nishu, oui, .userInteractive est le QoS utilisé pour le thread principal. Quel nom affiche-t-il ?

4 votes

Je ne pense pas que "ne JAMAIS appeler la fonction de synchronisation sur la file principale" soit correct. Il y a des cas où vous appelleriez la fonction sync dans le fil principal, par exemple lorsque vous avez un compteur global que vous avez besoin que chaque objet utilise et augmente le : dispatchQueue.sync { count += 1 ; self.orderId = count }

93voto

Andrey Chernukha Points 5262

Lorsque vous utilisez async il laisse la file d'attente appelante avancer sans attendre l'exécution du bloc distribué. Au contraire sync fera en sorte que la file d'attente appelante s'arrête et attende que le travail que vous avez distribué dans le bloc soit terminé. Par conséquent, sync est susceptible d'entraîner des blocages. Essayez d'exécuter DispatchQueue.main.sync de la file d'attente principale et l'application se figera parce que la file d'attente appelante attendra que le bloc distribué soit terminé mais elle ne pourra même pas démarrer (parce que la file d'attente est arrêtée et attend).

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

Exemple d'utilisation de la synchronisation :

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

0 votes

Est-ce que ce serait mal d'appeler DispatchQueue.main.sync à partir d'un fil de fond ?

1 votes

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

1 votes

@Honey L'une de ces situations consiste à mettre à jour une vue de collection de PHAssets à partir de l'API PhotoKit, comme le montre la documentation ici : developer.apple.com/documentation/photokit/

10voto

yoAlex5 Points 2350

GCD permet d'exécuter une tâche synchronously o asynchronously [À propos]

synchronous (bloquer et attendre) renvoie un contrôle quand la tâche sera terminée

asynchronous (dispatch and proceed) renvoie un contrôle immédiatement, en répartissant la tâche à lancer dans une file d'attente appropriée mais sans attendre qu'elle soit terminée.

[DispatchQueue]

3voto

Vipin Points 1174

sync o async Les méthodes n'ont aucun effet sur la file d'attente sur laquelle elles sont appelées.

sync bloquera le fil de qui l'appelle et non la file d'attente en qui l'appelle. Il s'agit de la propriété de DispatchQueue qui décide si le DispatchQueue attendra l'exécution de la tâche (file d'attente série) ou pourra exécuter la tâche suivante avant que la tâche actuelle ne soit terminée (file d'attente concurrente).

Donc même quand DispatchQueue.main.async est un appel asynchrone, une opération lourde ajoutée à celui-ci peut geler l'interface utilisateur car ses opérations sont exécutées en série sur le thread principal. Si cette méthode est appelée depuis le thread d'arrière-plan, le contrôle reviendra instantanément à ce thread, même si l'interface utilisateur semble figée. Ceci est dû au fait que async l'appel est effectué sur DispatchQueue.main

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X