146 votes

iPhone - Fil conducteur de Grand Central Dispatch

J'ai utilisé avec succès Grand Central Dispatch dans mes applications, mais je me demandais quel était le réel avantage d'utiliser quelque chose comme ça :

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

ou même

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

Je veux dire que dans les deux cas, vous déclenchez un bloc qui doit être exécuté 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 le moment où le bloc sera exécuté. J'ai vu des cas où des blocs étaient exécutés une demi-seconde après que vous les ayez lancés. Dans le second cas, c'est similaire à

[self doStuff];

n'est-ce pas ?

Je me demande ce que vous en pensez.

9 votes

Au fait, lancer une file d'attente principale dans un dispatch_sync entraînera un blocage.

5 votes

Je viens de le lire dans la docs : "Contrairement à dispatch_async, [dispatch_sync] ne revient pas avant la fin du bloc. L'appel de cette fonction et le ciblage de la file d'attente actuelle entraînent un blocage"... Mais peut-être que je lis cela mal... (la file d'attente actuelle ne signifie pas le thread principal). Veuillez corriger si je me trompe.

4 votes

@BrooksHanes pas toujours vrai. Il en résultera une impasse si vous êtes déjà sur le fil principal. Sinon, il n'y aurait pas d'impasse. Voir aquí

296voto

Robin Summerhill Points 10062

L'envoi d'un bloc vers la file d'attente principale est généralement effectué à partir d'une file d'attente d'arrière-plan pour signaler qu'un traitement d'arrière-plan est terminé, par ex.

- (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 dans une file d'attente en arrière-plan et nous 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 envoyons donc un signal à la file d'attente principale en utilisant un deuxième dispatch_async imbriqué.

Il existe probablement d'autres exemples où vous pourriez vouloir renvoyer la file d'attente principale, mais cela se fait généralement de cette manière, c'est-à-dire imbriqué dans un bloc envoyé à une file d'attente en arrière-plan.

  • traitement en arrière-plan terminé -> mise à jour de l'interface utilisateur
  • un morceau de données traité dans la file d'attente en arrière-plan -> signal à la file d'attente principale de commencer le morceau suivant
  • données réseau entrantes dans la file d'attente d'arrière-plan -> signalent à la file principale que le message est arrivé.
  • etc etc

Quant à la raison pour laquelle vous pourriez vouloir distribuer vers la file d'attente principale de la file d'attente principale... En général, vous ne le feriez pas, bien qu'il soit concevable que vous le fassiez pour planifier un travail à faire la prochaine fois dans la boucle d'exécution.

0 votes

Ah, je vois. Donc, j'ai raison. Il n'y a aucun avantage à le faire si vous êtes déjà dans la file d'attente principale, seulement si vous êtes dans une autre file d'attente et que vous voulez mettre à jour l'interface utilisateur. Merci.

0 votes

Je viens de modifier ma réponse pour expliquer pourquoi il n'est pas très utile de faire cela à partir de la file d'attente principale.

0 votes

De plus, je pense qu'il y a un bogue dans iOS 4 (qui pourrait avoir disparu dans iOS 5), où dispatch_sync à la file d'attente principale à partir du thread principal provoque un blocage, donc j'éviterais complètement de le faire.

17voto

bames53 Points 38303

Envoi de blocs à la file d'attente principale à partir du thread principal puede être utile. Elle donne à la file d'attente principale la possibilité de traiter d'autres blocs mis en file d'attente, de sorte que vous ne bloquez pas simplement l'exécution de tous les autres blocs.

Par exemple, vous pouvez écrire un serveur essentiellement monofilaire 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 rien d'autre que de passer sa vie à réagir à des événements, cela peut être tout à fait naturel. Il vous suffit de configurer vos gestionnaires d'événements pour qu'ils s'exécutent sur la file d'attente principale, puis d'appeler dispatch_main(), et vous n'aurez peut-être pas à vous soucier de la sécurité des threads.

11voto

timthetoolman Points 3753

J'espère que je comprends bien votre question, à savoir que vous vous interrogez sur les différences entre dispatch_async et dispatch_sync ?

dispatch_async

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

dispatch_sync

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

J'ai surtout utilisé un dispatch_async vers une file d'attente d'arrière-plan afin d'évacuer le travail de la file d'attente principale et de tirer parti de tous les cœurs supplémentaires dont dispose l'appareil. Ensuite, dispatch_async au thread principal si je dois mettre à jour l'interface utilisateur.

Bonne chance

1 votes

Merci, mais je demande quels sont les avantages d'envoyer quelque chose à la file principale, d'être sur la file principale.

9voto

weaselfloss1 Points 31

Il est notamment utile pour les activités de l'interface utilisateur, comme le réglage d'un compteur 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 processus et ne laissez pas UIKit démarrer le spinner.

- (void) handleDoSomethingButton{
     [mySpinner startAnimating];

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

renvoie le contrôle à la boucle d'exécution, qui planifie la mise à jour de l'interface utilisateur, le démarrage du spinner, puis passe à la chose suivante de la file d'attente de distribution, qui est le traitement réel. Lorsque le traitement est terminé, l'arrêt de l'animation est appelé, et vous retournez dans la boucle d'exécution, où l'interface utilisateur est alors mise à jour avec l'arrêt.

0 votes

@Jerceratops oui mais cela permet à la boucle d'exécution actuelle de se terminer.

3 votes

Oui, mais c'est quand même terrible. Ça bloque toujours l'interface utilisateur. Je pourrais appuyer sur un autre bouton juste après celui-ci. Ou essayer de faire défiler. "(faire quelque chose de long)" ne devrait pas se produire sur le thread principal, et dispatch_async pour laisser le clic du bouton "terminer" n'est pas une solution acceptable.

8voto

Niall Kiddle Points 761

Swift 3, 4 et 5

Exécution du code sur le fil principal

DispatchQueue.main.async {
    // Your code here
}

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