iPhone - Fil de discussion principal de Grand Central Dispatch

145

J'utilise avec succès, une grande répartition centrale dans mes applications, mais je me demandais quel est le véritable avantage d'utiliser quelque chose comme ceci:

dispatch_async(dispatch_get_main_queue(), ^{ ... do stuff

ou même

dispatch_sync(dispatch_get_main_queue(), ^{ ... do stuff

Je veux dire, dans les deux cas, vous déclenchez un bloc à exécuter sur le thread principal, exactement là où l'application s'exécute et cela n'aidera pas à réduire la charge. Dans le premier cas, vous n'avez aucun contrôle sur l'exécution du bloc. J'ai vu des cas de blocs exécutés une demi-seconde après que vous les ayez tirés. Le deuxième cas, il est similaire à

[self doStuff];

droite?

Je me demande ce que vous en pensez.

canard
la source
9
À propos, lancer une file d'attente principale dans un dispatch_sync entraînera un blocage.
Brooks Hanes
5
Lisez-le simplement dans la documentation: "Contrairement à dispatch_async, [dispatch_sync] ne retourne pas tant que le bloc n'est pas terminé. L'appel de cette fonction et le ciblage de la file d'attente actuelle entraîne un blocage." ... Mais peut-être que je lis mal ... ( la file d'attente actuelle ne signifie pas le thread principal). Veuillez corriger si je me trompe.
Brooks Hanes
4
@BrooksHanes pas toujours vrai. Cela entraînera un blocage si vous êtes déjà sur le thread principal. Sinon, il n'y aurait pas de blocage. Voir ici
Honey

Réponses:

296

La distribution d'un bloc vers la file d'attente principale est généralement effectuée à partir d'une file d'attente en arrière-plan pour signaler que certains traitements en arrière-plan sont terminés, par exemple.

- (void)doCalculation
{
    //you can use any string instead "com.mycompany.myqueue"
    dispatch_queue_t backgroundQueue = dispatch_queue_create("com.mycompany.myqueue", 0);

    dispatch_async(backgroundQueue, ^{
        int result = <some really long calculation that takes seconds to complete>;

        dispatch_async(dispatch_get_main_queue(), ^{
            [self updateMyUIWithResult:result];
        });    
    });
}

Dans ce cas, nous effectuons un long calcul sur une file d'attente en arrière-plan et devons mettre à jour notre interface utilisateur lorsque le calcul est terminé. La mise à jour de l'interface utilisateur doit normalement être effectuée à partir de la file d'attente principale, nous renvoyons donc un signal à la file d'attente principale en utilisant un deuxième dispatch_async imbriqué.

Il y a probablement d'autres exemples où vous voudrez peut-être renvoyer à la file d'attente principale, mais cela se fait généralement de cette manière, c'est-à-dire imbriqué à partir d'un bloc distribué à une file d'attente en arrière-plan.

  • traitement en arrière-plan terminé -> mettre à jour l'interface utilisateur
  • morceau de données traité sur la file d'attente d'arrière-plan -> signaler la file d'attente principale pour démarrer le morceau suivant
  • données réseau entrantes sur la file d'attente d'arrière-plan -> signalent la file d'attente principale que le message est arrivé
  • etc

Quant à savoir pourquoi vous voudrez peut-être envoyer à la file d' attente principale à partir de la file d'attente principale ... Eh bien, vous ne le feriez généralement pas bien que vous puissiez le faire pour planifier un travail à faire la prochaine fois dans la boucle d'exécution.

Robin Summerhill
la source
Ah, je vois. Alors, j'ai raison. Il n'y a aucun avantage à faire cela si vous êtes déjà dans la file d'attente principale, juste si vous êtes sur une autre file d'attente et que vous souhaitez mettre à jour l'interface utilisateur. Merci.
Canard
Je viens de modifier ma réponse pour expliquer pourquoi il n'est pas très utile de le faire à partir de la file d'attente principale.
Robin Summerhill
De plus, je pense qu'il y a un bogue dans iOS 4 (peut-être disparu dans iOS 5), où dispatch_sync vers la file d'attente principale à partir du thread principal provoque simplement un blocage, alors j'éviterais de le faire entièrement.
joerick
10
Ce n'est pas un bug, c'est le comportement attendu. Comportement pas très utile certes, mais vous devez toujours être conscient des blocages lors de l'utilisation de dispatch_sync. Vous ne pouvez pas vous attendre à ce que le système vous protège des erreurs du programmeur tout le temps.
Robin Summerhill
2
Qu'est-ce que backgroundQueue ici? Comment créer un objet backgroundQueue
Nilesh Tupe
16

La distribution de blocs vers la file d'attente principale à partir du thread principal peut être utile. Cela donne à la file d'attente principale une chance de gérer d'autres blocs qui ont été mis en file d'attente afin de ne pas simplement empêcher tout le reste de s'exécuter.

Par exemple, vous pouvez écrire un serveur essentiellement à thread unique qui gère néanmoins de nombreuses connexions simultanées. Tant qu'aucun bloc individuel dans la file d'attente ne prend trop de temps, le serveur reste réactif aux nouvelles demandes.

Si votre programme ne fait que passer sa vie entière à réagir aux événements, cela peut être tout à fait naturel. Vous venez de configurer vos gestionnaires d'événements pour qu'ils s'exécutent sur la file d'attente principale, puis appelez dispatch_main (), et vous n'aurez peut-être pas du tout à vous soucier de la sécurité des threads.

bames53
la source
11

J'espère que je comprends correctement votre question en ce que vous vous interrogez sur les différences entre dispatch_async et dispatch_sync?

dispatch_async

enverra le bloc dans une file d'attente de manière asynchrone. Cela signifie qu'il enverra le bloc dans la file d'attente et n'attendra pas son retour avant de continuer l'exécution du code restant dans votre méthode.

dispatch_sync

enverra le bloc dans une file d'attente de manière synchrone. Cela empêchera toute exécution supplémentaire du code restant dans la méthode jusqu'à ce que le bloc ait fini de s'exécuter.

J'ai principalement utilisé une dispatch_asyncfile d'attente en arrière-plan pour extraire le travail de la file d'attente principale et profiter de tous les cœurs supplémentaires que l'appareil peut avoir. Puis dispatch_asyncau fil de discussion principal si j'ai besoin de mettre à jour l'interface utilisateur.

Bonne chance

timthetoolman
la source
1
merci, mais je demande quels sont les avantages d'envoyer quelque chose à la file d'attente principale, d'être dans la file d'attente principale.
Canard
9

Un endroit où c'est utile est pour les activités de l'interface utilisateur, comme la définition d'un spinner avant une longue opération:

- (void) handleDoSomethingButton{

    [mySpinner startAnimating];

    (do something lengthy)
    [mySpinner stopAnimating];
}

ne fonctionnera pas, car vous bloquez le thread principal pendant votre long travail et ne laissez pas UIKit démarrer réellement le spinner.

- (void) handleDoSomethingButton{
     [mySpinner startAnimating];

     dispatch_async (dispatch_get_main_queue(), ^{
          (do something lengthy)
          [mySpinner stopAnimating];
    });
}

retournera le contrôle à la boucle d'exécution, qui planifiera la mise à jour de l'interface utilisateur, en démarrant le spinner, puis obtiendra la chose suivante de la file d'attente d'expédition, qui est votre traitement réel. Lorsque votre traitement est terminé, l'arrêt de l'animation est appelé et vous revenez à la boucle d'exécution, où l'interface utilisateur est ensuite mise à jour avec l'arrêt.

weaselfloss1
la source
@Jerceratops oui mais cela permet au runloop actuel de se terminer.
Dan Rosenstark
3
Oui, mais c'est toujours terrible. Il bloque toujours l'interface utilisateur. Je pourrais appuyer sur un autre bouton juste après celui-ci. Ou essayez de faire défiler. "(faire quelque chose de long)" ne devrait pas se produire sur le thread principal, et dispatch_async pour laisser le bouton cliquer sur "terminer" n'est pas une solution acceptable.
Jerceratops
8

Swift 3, 4 et 5

Exécution de code sur le thread principal

DispatchQueue.main.async {
    // Your code here
}
Niall Kiddle
la source
1

Async signifie asynchrone et vous devez l'utiliser la plupart du temps. Vous ne devriez jamais appeler la synchronisation sur le thread principal car cela bloquera votre interface utilisateur jusqu'à ce que la tâche soit terminée. Vous Voici une meilleure façon de faire cela dans Swift:

runThisInMainThread { () -> Void in
    // Run your code like this:
    self.doStuff()
}

func runThisInMainThread(block: dispatch_block_t) {
    dispatch_async(dispatch_get_main_queue(), block)
}

Il est inclus en tant que fonction standard dans mon repo, vérifiez-le: https://github.com/goktugyil/EZSwiftExtensions

Esqarrouth
la source