Comprendre dispatch_async

233

J'ai une question sur ce code

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSData* data = [NSData dataWithContentsOfURL: 
      kLatestKivaLoansURL];
    [self performSelectorOnMainThread:@selector(fetchedData:) 
      withObject:data waitUntilDone:YES];
});

Le premier paramètre de ce code est

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 

Demandons-nous à ce code d'effectuer des tâches série sur la file d'attente globale dont la définition est elle-même qu'elle renvoie la file d'attente simultanée globale d'un niveau de priorité donné?

Quel est l'avantage d'utiliser dispatch_get_global_queuesur la file d'attente principale?

Je suis confus. Pourriez-vous s'il vous plaît m'aider à mieux comprendre cela.

user2332873
la source
1
Vous devriez mieux couper votre code en plusieurs lignes afin que cela ait plus de sens. sécurisez votre dispatch_get_global_queueintérieur un type variable de dispatch_queue_t myQueue. Son plus lisible en passant uniquement myQueue à votre `` dispatch_async`
Alex Cio

Réponses:

517

La principale raison pour laquelle vous utilisez la file d'attente par défaut sur la file d'attente principale est d'exécuter des tâches en arrière-plan.

Par exemple, si je télécharge un fichier depuis Internet et que je souhaite mettre à jour l'utilisateur sur la progression du téléchargement, je vais exécuter le téléchargement dans la file d'attente prioritaire par défaut et mettre à jour l'interface utilisateur dans la file d'attente principale de manière asynchrone.

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    //Background Thread
    dispatch_async(dispatch_get_main_queue(), ^(void){
        //Run UI Updates
    });
});
David
la source
Je comprends que David vous remercie pour votre réponse, mais ma question était plutôt de comprendre la logique de le faire, c'est-à-dire de demander ce code pour effectuer des tâches série sur la file d'attente globale qui est la file d'attente simultanée elle
user2332873
Je fais exactement ce que vous suggérez, mais d'une certaine manière, uiTableViewCell ne se met pas à jour immédiatement lorsque j'appelle [self.tableView reloadData] dans les mises à jour de l'interface utilisateur. Cela prend environ 4 ou 5 secondes. Cela me rend fou depuis plusieurs jours maintenant .
GrandSteph
@GrandSteph Je ne connais pas trop cette méthode. Peut-être que cette méthode ne prend que 5 secondes à exécuter. L'important avec dispatch_async est qu'il vous permet de faire des choses en arrière-plan sans suspendre le thread principal.
David
2
qu'est-ce que cela 0signifie?
Honey
3
@Honey Le 0 est le flagsparamètre, qui actuellement ne fait absolument rien. De la documentation:Flags that are reserved for future use. Always specify 0 for this parameter.
David
199

Toutes les files d'attente DISPATCH_QUEUE_PRIORITY_X sont des files d'attente simultanées (ce qui signifie qu'elles peuvent exécuter plusieurs tâches à la fois) et sont FIFO dans le sens où les tâches dans une file d'attente donnée commenceront à s'exécuter en utilisant l'ordre "premier entré, premier sorti". Ceci est en comparaison avec la file d'attente principale (de dispatch_get_main_queue ()), qui est une file d'attente série (les tâches commenceront à s'exécuter et finiront de s'exécuter dans l'ordre dans lequel elles sont reçues).

Ainsi, si vous envoyez 1000 blocs dispatch_async () à DISPATCH_QUEUE_PRIORITY_DEFAULT, ces tâches commenceront à s'exécuter dans l'ordre dans lequel vous les avez envoyées dans la file d'attente. De même pour les files d'attente HIGH, LOW et BACKGROUND. Tout ce que vous envoyez dans l'une de ces files d'attente est exécuté en arrière-plan sur des threads alternatifs, loin de votre thread d'application principal. Par conséquent, ces files d'attente conviennent à l'exécution de tâches telles que le téléchargement en arrière-plan, la compression, le calcul, etc.

Notez que l'ordre d'exécution est FIFO par file d'attente. Donc, si vous envoyez 1000 tâches dispatch_async () aux quatre files d'attente simultanées différentes, en les répartissant uniformément et en les envoyant dans BACKGROUND, LOW, DEFAULT et HIGH dans l'ordre (c'est-à-dire que vous planifiez les 250 dernières tâches dans la file d'attente HIGH), il est très probable que les premières tâches que vous voyez commencer seront sur cette file d'attente ÉLEVÉE car le système a pris votre implication que ces tâches doivent arriver au CPU le plus rapidement possible.

Notez également que je dis "commencera à exécuter dans l'ordre", mais gardez à l'esprit que les files d'attente simultanées ne termineront pas nécessairement l'exécution dans l'ordre en fonction de la durée de chaque tâche.

Selon Apple:

https://developer.apple.com/library/content/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html

Une file d'attente de répartition simultanée est utile lorsque vous avez plusieurs tâches pouvant s'exécuter en parallèle. Une file d'attente simultanée est toujours une file d'attente dans la mesure où elle supprime les tâches dans un ordre premier entré, premier sorti; cependant, une file d'attente simultanée peut retirer des tâches supplémentaires avant la fin des tâches précédentes. Le nombre réel de tâches exécutées par une file d'attente simultanée à un moment donné est variable et peut changer dynamiquement à mesure que les conditions de votre application changent. De nombreux facteurs affectent le nombre de tâches exécutées par les files d'attente simultanées, notamment le nombre de cœurs disponibles, la quantité de travail effectuée par d'autres processus, ainsi que le nombre et la priorité des tâches dans d'autres files d'attente de répartition en série.

Fondamentalement, si vous envoyez ces 1000 blocs dispatch_async () à une file d'attente DEFAULT, HIGH, LOW ou BACKGROUND, ils commenceront tous à s'exécuter dans l'ordre que vous leur envoyez. Cependant, les tâches plus courtes peuvent se terminer avant les plus longues. Cela s'explique par le fait qu'il existe des cœurs de processeur disponibles ou si les tâches de file d'attente actuelles effectuent un travail non intensif sur le plan informatique (ce qui fait penser au système qu'il peut répartir des tâches supplémentaires en parallèle indépendamment du nombre de cœurs).

Le niveau de concurrence est entièrement géré par le système et est basé sur la charge du système et d'autres facteurs déterminés en interne. C'est la beauté de Grand Central Dispatch (le système dispatch_async ()) - vous faites simplement vos unités de travail en tant que blocs de code, définissez une priorité pour elles (en fonction de la file d'attente que vous choisissez) et laissez le système gérer le reste.

Donc, pour répondre à votre question ci-dessus: vous avez partiellement raison. Vous "demandez ce code" pour effectuer des tâches simultanées sur une file d'attente simultanée globale au niveau de priorité spécifié. Le code du bloc s'exécutera en arrière-plan et tout code supplémentaire (similaire) s'exécutera potentiellement en parallèle en fonction de l'évaluation par le système des ressources disponibles.

La file d'attente "principale" d'autre part (de dispatch_get_main_queue ()) est une file d'attente série (non simultanée). Les tâches envoyées à la file d'attente principale s'exécuteront toujours dans l'ordre et se termineront toujours dans l'ordre. Ces tâches seront également exécutées sur le thread d'interface utilisateur, il est donc approprié pour mettre à jour votre interface utilisateur avec des messages de progression, des notifications d'achèvement, etc.

SimplePanda
la source
+1, mais je pense qu'en pratique, peu importe que les files d'attente simultanées soient FIFO ou simplement aléatoires. Si vous lancez 5 tâches en boucle, supposez qu'elles démarrent essentiellement en même temps. Rien ne garantit par exemple que la première opération d'E / S de la 1ère tâche aura lieu avant la 5e, même si elles exécutent le même code. OTOH, pour les files d'attente série, le comportement FIFO est essentiel et à mon humble avis, c'est la différence déterminante entre les deux types de files d'attente.
Gerhard Wesp
Explication incrédile. Applaudit beaucoup!
Okhan Okbay
36

Version Swift

Ceci est la version Swift de la réponse Objective-C de David. Vous utilisez la file d'attente globale pour exécuter des tâches en arrière-plan et la file d'attente principale pour mettre à jour l'interface utilisateur.

DispatchQueue.global(qos: .background).async {
    
    // Background Thread
    
    DispatchQueue.main.async {
        // Run UI Updates
    }
}
Suragch
la source