Multithreading et parallélisme dans mon application iOS en Swift
Jérémy
Membre
Bonjour à tous,
Dans l'application que je suis en train de développer, j'aimerais pouvoir exécuter de façon simultanée deux blocs de code. Pour information, les deux bloc ne sont pas dépendant l'un de l'autre. Voici la façon dont je m'y suis pris :
func myFunc() {
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0)) {
aBloc()
}
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0)) {
bBloc()
}
}
Quand je vais des tests via le simulateur, je n'ai pas l'impression que les deux algos s'exécutent en même temps. Mon impression est elle bonne ? Ceci est il normal ? Si oui, que dois-je faire ?
Merci pour vos réponses !
Mots clés:
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Disons que les blocks vont être exeÌcuteÌs de manière concurrente mais dans leur ordre d'arriveÌe.
Ici on ajoute aBloc → on commence l'exec. Dans le meÌ‚me temps bBloc est ajouteÌ â†’ on commence l'exec.
Il est possible que le deÌbut de bBloc arrive après la fin de aBloc. Dans ce cas tu as l'impression que ça n'est pas concurrent.
Si les 2 blocs sont assez longs à exeÌcuter tu devrais voir une concurrence.
Si tu nous en dit plus sur ces blocs et ce qu'ils sont censeÌs faire on va peut-eÌ‚tre pouvoir mieux t'aider.
Merci pour ta réponse Pyroh !
Je vais rentrer un peu plus en détail. Je fais des calcul scientifiques (dans le domaine de l'astronomie) qui doivent être effectués de façon régulière (au moins toute les secondes). Pour réduire le temps de réponse d'affichage de tous les résultats, je me suis dit qu'il serait mieux de paralléliser les calculs.
Exemple, dans le cas du calcul de la position en temps réel de la planète mercure (à l'instant T) j'aimerai lancer aussi en simultanée l'algorithme qui calculera l'heure et lever et un troisième algorithme pour l'heure du coucher (jour le jour courant). Chaque algorithme n'a pas besoin de la réponse des autres pour pouvoir être exécuté. Il y a des calculs plus ou moins long. Mais pour que je puisse actualiser les données en temps réel, j'aimerais réduire au maximum le temps de rafraà®chissement de l'affichage des valeurs. ::)
Je ne sais pas si j'ai été très clair mais si tu as d'autres questions pour que tu aies plus de précision, n'hésite pas ! Merci encore !
Ta réponse est claire rassure-toi.
Maintenant l'ideÌal serait d'arrêter d'avoir des impressions et en bon scientifique que tu es et se reposer sur une méthode un peu plus empirique.
Lance tes 3 algos comme tu le fais jusque là mais au deÌbut de chacun tu mets un print("start algo x") et à la fin print("end algo x"). Ensuite tu regarde le résultat dans la console et tu pourras deÌduire de toi meÌ‚me si l'exeÌcution est concurrente ou séquentielle.
Attention que tout ce qui est logs (NSLog, print, ...) et ce qui est mà j d'UI doivent se faire sur la main queue :
Merci Pyroh !
Justement, qu'elle est la différence d'un point de vue exécution entre un :
et un
?
dispatch_get_main_queue() renvoie une queue qui s'exécute dans le thread principal de l'appli. Dans ton cas, étant donné que tu veux mettre en concurrence tes calculs, il te faut taper dispatch_get_global_queue().
Un peu de lecture sur GCD...
https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/
La main queue ne doit être utiliseÌe dans ton cas que pour mettre à jour l'UI UI ou logger dans la console. Sinon comme Mala le dit il faut utiliser dispatch_get_global_queue(). (Mala il est fort pour le multi-threading)
Okay, merci à vous deux !
Petite dernière question. Comment faut il faire pour pourvoir exécuter en parallèle deux algorithmes, récupérer les deux valeurs pour pouvoir les additionner et appliquer un calcul sur la somme.
Code (faux) qui illustre mes propos :
Tu peux faire avec les NSOperation.
Et sinon, il y a aussi les dispatch_group que j'aime bien.
On créer un groupe: dispatch_group_t group = dispatch_group_create()
On lance les blocs: dispatch_group_async()
On attend la fin d'exécution: dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
A voir ici pour culture...
http://amro.co/gcd-using-dispatch-groups-for-fun-and-profit
Edit: à voir pour la version Swift mais cela va être dans le même esprit.
Mala, peux-tu nous mettre un schéma/exemple rapide avec les dispatch group ?
Merci colas ! 8--)
J'imagine qu'il suffise que je transpose ton exemple en Swift et le tour sera joué.
Merci bien !
Oui, mais je ne swifte pas. Je ne sais pas si la balise __block existe en swift.
Ici elle est importante.
Sinon en Swift ça donne ça :
Merci pour le tuyau Pyro !
Et tu sais comment que les calculs sont terminées ?
Tu le sais pas. Tu le sais pas plus que dans le code de colas_ d'ailleurs.
J'ai simplement traduit ligne par ligne en Swift en évitant simplement un Warning.
Lis les codes.
Avec
finalOperation ne sera pas lancée tant que ses dépendances ne sont pas terminées.
Donc finalOperation doit prévenir l'application que les calculs sont terminés avec une notification ?
Il me semble que NSOperationQueue propose waitUntilAllOperationsAreFinished non? Dans ce cas, nul besoin de finalOperation. On termine en synchrone dans le main thread.
Ouais mais dans un cas concret on va bloquer le main thread et si les calculs sont longs on va bloquer l'UI.
C'est surtout pratique si tu fais des operations qui ne durent pas trop longtemps comme des appels à des outils console par exemple et dont tu veux récupérer la sortie standard.
Je vois pas quoi dire de plus que le lien que j'ai mis.
Par exemple cela peut donner un truc comme ça en Obj-C...
A noter que dans cet exemple issu d'un code à moi, je bride le nombre de threads avec un sémaphore. Je peux ainsi limiter l'usage mémoire à façon sur des calculs gourmands.
J'avais lu quelque part que dispatch_sync est plutôt à proscrire pour éviter les dead lock.
Or, waitUntilAllOperationsAreFinished est plus ou moins un dispatch_sync.
On est bien d'accord. En même temps là si ses calculs sont long (et j'ai un doute tout de même) il risque de relancer plus vite qu'il n'a les retours. Donc conceptuellement cela demande d'en savoir plus.
Je sais que j'ai déjà rencontré un cas effectivement où cela posait souci mais ça remonte trop loin pour me souvenir du contexte. Je n'utilise plus que GCD en direct. Cela m'épargne une surcouche d'abstraction pour rien.
@mala : si tu ne veux pas bloquer le thread, comment ferais-tu ?
Tu placerais tout le code
dans un dispatch_async(queue, ^{}); (avec la même queue) ?
Dans le lien que j'ai mis avant c'est évoqué. Au lieu de se mettre en attente, on passe juste par dispatch_group_notify() pour avoir une notification asynchrone.
A priori, quand je dois faire du calcul parallèle avec une conclusion, j'utilise la méthode donnée plus haut (NSOperation).
Tu ne m'as pas convaincu avec les dispatch_group (je trouve le code moins lisible) d'autant plus qu'il faut s'abonner à une notification si j'ai bien compris.
Je vois pas ce qui te gêne. dispatch_group_notify() fonctionne avec un simple bloc. ???
Fonctionnellement ta proposition est viable. C'est juste peu élégant (créer une NSOpération avec dépendance juste pour ça c'est capilotracté à mon sens) en plus d'être moins performant.
Ok, je pensais que dispatch_group_notify() fonctionnait avec NSNotificationCenter.