IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Windows Presentation Foundation Discussion :

boucle très lente dans un thread


Sujet :

Windows Presentation Foundation

  1. #1
    Membre du Club
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Octobre 2015
    Messages
    98
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Santé

    Informations forums :
    Inscription : Octobre 2015
    Messages : 98
    Points : 59
    Points
    59
    Par défaut boucle très lente dans un thread
    bonsoir.
    Je suis en cours de portage d'une application console vers WPF.
    La notion de thread n'a pas été simple a assimiler et je suis parti sur un backgroundworker qui fonctionne bien pour ce que je veut faire (même si pour une simple mise a jour des variables d'un thread long qui boucle indéfiniment, sur le thread UI une autre solution aurais peut être était plus propre).

    Mon soucis , le processus, c'est a dire un tour de boucle était réaliser auparavant en 500ms max en mode console et a présent en WPF je suis a 12sec ! Y'a comme qui dirait un méga soucis
    Visiblement en arrêt au curseur j'ai isolé dans mon thread long une boucle interne while d'environ 60 tour (c'est volontaire) qui serait la source de ce ralentissement (le reste du processus se déroule en 500ms environ).

    Le soucis c'est que chaque méthode dans cette boucle se déroule sans soucis , c'est la succession des tour qui visiblement est la cause de cette lenteur. J'ai pas mal de méthode static (pas bien !) que j'ai converti en non static pour le WPF mais de ce que j'ai lu le ralentissement n'a rien a voir avec ça donc je sèche un peu. Je me suis plongé dans cette boucle pour essayer de comprendre d'ou viens le soucis mais j'ai du mal a me lancer car je ne maîtrise pas bien les systèmes de thread donc si ça se trouve je vais creuser la ou il ne faut pas.

    Je pose mon code background (qui fonctionne donc.... mais lentement) juste histoire d'être certain que vous ne voyez pas d'erreur qui pourrait provoquer ce ralentissement. Pour la boucle en question elle est noyer dans trop de truc pour que j'ose la présenter ici (on a son petit orgueil) donc juste si vous avez une idée de truc classique de débutant qui ralentissent fortement une boucle while j’achète !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
     
    public partial class MainWindow : Window
        {
            // methode utiliser dans le thread long et parfois dans l'affichage sur le thread UI           
            public Affichage affichage { get; set;}       
            public PonderationClass ponderationClass { get; set; }
            //....        
            public SelectAction selection { get; set; }        
            public Repartition repartition { get; set; }
     
            //variable présente dans méthode longue et qui serons mise a jour dans l'affichage
            int Jours;
            int Heures;
            int Minutes;
            // .... 
            public int FreqCar;        
            string TheTexte;
            public double E_Demander;
     
     
            private BackgroundWorker backgroundWorker;
            ManualResetEvent _busy = new ManualResetEvent(true);
     
            public MainWindow()
            {
                InitializeComponent();          
            }
     
            private void Window_Loaded(object sender, RoutedEventArgs e)
            {          
                backgroundWorker = new BackgroundWorker();
                backgroundWorker.DoWork += BackgroundWorkerDoWork;
                backgroundWorker.RunWorkerCompleted += BackgroundWorkerRunWorkerCompleted;
     
            }
     
            void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
            {
                while (MetaBasal > 0)  // juste pour que le processus long tourne indéfiniment mais la valeur de metabasal est une constante
                {
                    _busy.WaitOne(Timeout.Infinite);
     
                    The_Master();      // ma methode longue!!!!         
                    Thread.Sleep(50); // un intérêt ici ? Vu qu'il ne doit pas y avoir de mise a jour de l'affichage tend que la méthode n'est pas terminer  
     
                    // NORMALEMENT PAS D'INVOKE TEND QUE LA METHODE LONGUE N'EST PAS TERMINER 
                    Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(
                                      delegate ()
                                      {
                                          // ici une 40aine de variable a extraire du thread long pour affichage et réutilisation de ces variables pour la boucle suivante                                     
                                          labcycle.Content = ncycle;
                                          labnyctemere.Content = Nyctemere;
                                          labJour.Content = Jours;
                                          labHeure.Content = Heures;
                                          // ...
                                          labminute.Content = Minutes;
                                          labtheTexte.Content = TheTexte;
                                          labResultat.Content = Resultat;                                      
                                          labenergdemand.Content = E_Demander;
                                          labfreqcardeffort.Content = bpm_Card;
     
                                          Thread.Sleep(5); // je le pose la sans savoir si sa a un intérêt... suffisant pour mettre a jour l'affichage
                                      }));                
                }
     
                // création d'une sortie pour la fin du backgroundworker mais qui logiquement ne devrait jamais se produire               
                List<object> genericlist = new List<object>();
                genericlist.Add(ncycle);
                genericlist.Add(Nyctemere);
                genericlist.Add(Jours);
                genericlist.Add(Heures);
                genericlist.Add(Minutes);
                genericlist.Add(TheTexte);
                genericlist.Add(Resultat);
                genericlist.Add(FreqCar);
                //........................
                e.Result = genericlist;
            }
     
            void BackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                List<object> result = e.Result as List<object>;
                labcycle.Content = result[0];
                //..... 
                // Qui n'arrivera jamais.... d'ou l'utilité d'utiliser un backgroundworker du coup...
            }
     
            private void BtnLancer(object sender, RoutedEventArgs e) // Lance process long
            {
                if (!this.backgroundWorker.IsBusy)
                    this.backgroundWorker.RunWorkerAsync();               
            }
     
            private void BtnPause(object sender, RoutedEventArgs e) // met en Pause et relance process long
            {
                if (bt1.Content.ToString() == "PAUSE")
                {
                    _busy.Reset();
                    bt1.Content = "RESUME";
                }
                else
                {
                    _busy.Set();
                    bt1.Content = "PAUSE";
                }
            }
     
            private void Btnstop(object sender, RoutedEventArgs e) // stop process long
            {
                //if (this.backgroundWorker.IsBusy)
                //this.backgroundWorker.CancelAsync();
                _busy.Reset();
                //backgroundWorker.Dispose();
                // Entre les 3 je ne sais pas quoi prendre mais l'arret sauvage de mon banc d'essais n'est pas un probleme
     
            }
        }

    Et voici l'endroit ou se trouve la boucle maudite dans mon thread long qui augmente la durée du process
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    public partial class MainWindow : Window
        {        
            public void The_Master()
            {
              // ici on est dans la méthode longue  
              // .....
     
                // on arrive sur une boucle while qui dure près de 12sec...
                while (FreqCar > 0)
                {                
                    // une dizaine de méthode qui ne sont plus propre au thread long mais a toute l'application histoire de rendre accessible les variable a main window 
                    Metabolisme.Gl...
                    Xeros = Meta...
                    Xeros_MB = Me...
     
                    // ...
     
                    FreqCar--;                                           
                } 
            }
    }
    voila... on bute karadoc...

    bonne soirée

  2. #2
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    plusieurs choses :

    déjà si tu as des lignes de code dont tu n'es pas sur de l’intérêt c'est que tu ne les comprends pas
    et utiliser du code qu'on ne comprend pas ce n'est pas normal
    c'est le cas sur tes thread.sleep
    bon j'imagine que tu comprends ce que ca fait, dans ce cas pourquoi tu te demandes si c'est utile ?
    donc supprime tes threads.sleep, à moins que tu ne veuilles perdre du temps volontairement ...

    après quand on du code long, il convient d'identifier les parties longues
    pour ca il y a le system.diagnostics.stopwatch qui est un chronomètre
    sur du code qui peut être rapide mais qui est exécuté plein de fois il faut additionner tous les temps pour avoir une idée de la répartition

    donc à ta place j'instancierais 2 stopwatch, avec .Start et .Stop autour de The_Master(); pour le 1er et autour de invoke pour le 2ème
    tu laisses passer quelques tours et tu fais pause, et tu regardes .ElapsedMilliseconds sur chaque
    si c'est the_master qui prend plus de 90% du temps alors c'est par là qu'il faudra chercher ensuite


    tu expliques que ton code n'arrivera jamais dans completed, pourquoi avoir ce code alors ?
    l'utilité du backgroundworker c'est que ses event reportprogress et completed sont sur le thread principal, ce qui évite d'avoir des invokes
    tu as les 2 donc tu as raté un truc
    soit tu utilises un thread normal et des invoke, soit un bgw et pas d'invoke
    quand tu appelles reportprogress tu peux passer une variable, qui sera transmise à l'event idoine ; une seule variable certes, mais elle peut etre de n'importe quel type (list<quelquechose> ou une classe avec des propriétés)

    si ta boucles fait la même chose que sur l'appli console elle n'a aucune raison d'être plus longue
    mettre à jour l'interface je ne pense pas que c'est bien long

    une méthode static ce n'est pas forcément le mal

    tu as plein de variables qui sont surement settées par le thread, et qui servent pour l'interface
    ca manque de POO, il te faudra une ou plusieurs classes avec des propriétés
    si tu es en WPF en plus il y a des mécanismes de binding, qui fait que tu n'as pas à mettre à jour l'interface, elle se met à jour automatiquement, ce qui réduirait ton code
    (il faut quand même inotifypropertychanged ou des dependencyproperty)

    et ton genericlist ca ne se fait pas de mettre des variables dans un ordre précis

    bref si ca t'arranges tu m'envoies ton code (tu peux retirer une partie de l'algo si nécessaire) et je te le rends en POO
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  3. #3
    Membre du Club
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Octobre 2015
    Messages
    98
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Santé

    Informations forums :
    Inscription : Octobre 2015
    Messages : 98
    Points : 59
    Points
    59
    Par défaut
    Salut et un grand merci d'avoir pris le temps d'écrire tous ça .

    Sur tes conseil j'ai refondu toute l'histoire du thread (d'ou le temps pour repondre et ça donne ceci:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
     
    ManualResetEvent _busy = new ManualResetEvent(true);
    private delegate void deleguer();
    public Thread thread;
     
            Stopwatch SChrono = new Stopwatch();
     
            public MainWindow()
            {
                InitializeComponent();          
            }
     
            private void Window_Loaded(object sender, RoutedEventArgs e)
            {
     
                // creation d'un thread long
                this.thread = new Thread(ProcessLong);
            }
     
            private void BtnLancer(object sender, RoutedEventArgs e) // Lance process long
            {           
                thread.Start();
            }
     
            private void BtnPause(object sender, RoutedEventArgs e) // met en Pause et relance process long
            {
                if (bt1.Content.ToString() == "PAUSE")
                {
                    _busy.Reset();
                    bt1.Content = "RESUME";
                }
                else
                {
                    _busy.Set();
                    bt1.Content = "PAUSE";
                }
            }
     
            private void Btnstop(object sender, RoutedEventArgs e) // stop process long
            { 
                Application.Current.Shutdown();
            }
     
            public void ProcessLong()
            {
                while (MetaBasal > 0)
                {
                    _busy.WaitOne(Timeout.Infinite);
                    deleguer maitre = The_Master;
                    maitre();                
                    Dispatcher.Invoke(DispatcherPriority.Background, new Action(
                                      delegate ()
                                      {
                                          labdecision.Text = Decision;
                                          labcycle.Content = ncycle;
                                          labnyctemere.Content = Nyctemere;
                                          labJour.Content = Jours;
                                          labHeure.Content = Heures;
                                          labminute.Content = Minutes;
                                          //.....
                                          labmetabobase.Content = MetaBasal;
                                          labenergdemand.Content = E_Demander;
                                          labfreqcardeffort.Content = bpm_Card;
                                      }));
                    Thread.Sleep(100);
                }            
            }
    Je suis pas peu fier !!!!
    Donc mon thread long est composé de la méthode The_Master() qui contient tout mon ancien process (qui était en mode console) et un delegate pour sortir les variables a mettre à jour dans l'affichage.
    La boucle while(metabasal) me sert a a faire une boucle a l'infini. C'est correct ou ça pue encore le bidouillage ?

    J'ai donc encore 2 interrogations:
    -Est-ce que selon ce code je peut être certains que le delegate dispose de suffisamment de temps pour afficher? C'est pas encore clair dans ma tête mais il n'y a pas de risque que The_Master soit a la 36 000ème boucle et que l'affichage (ce que je voit sur l'écran) n'en soit qu'a la 2ème mise a jour de l'affichage? Ils sont sur le même thread donc logiquement la boucle ne se relance qu'a la fin du delegate donc de l'affichage c'est bien ça ?

    -Grace a ton Stopwatch (trop bien le truc) j'ai pu isoler cette fameuse boucle dans The_Master() qui prend temps de temps. Bon, je rappelle qu'en mode console on devait être sur sur 200ms max pour le process complet. La juste la boucle elle prend 18000ms... J'ai isoler une des méthode qui est dedans que voici :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Metabolisme.G_Sang(Metabo); // tel que appeler dans la boucle
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     
    public static void G_Sang(Globale Metabo)  // en Gr, En gr
            {
                double VolumeSanguin = 6.0; 
                double Glycémie = Metabo.Glucose_Sanguin;
                string Etat_Glycemique = "";
     
                if (Glycémie > 1.0)
                {
                    Etat_Glycemique = "+ + + [HYPER-Glycémie]";
                }
                else if (Glycémie < 0.5)
                {
                    Etat_Glycemique = "- - - [HYPO-Glycémie]";
                }
                else
                {
                    Etat_Glycemique = "/ / / [Normoglycémie]";
                }
     
                Console.WriteLine(Environment.NewLine);
                Console.WriteLine("             " + Etat_Glycemique + " à {0:F10} g/L", Glycémie);
                Console.WriteLine("                         Lipidémie à {0:F10} g/L", Metabo.Lipide_Sanguin / 6);
                Console.WriteLine(Environment.NewLine);
            }
    resultat = 1800ms !!!!!! Faut pas abuser ça devrait prendre moins quand même

    Est-ce que ce sont les appel Consoles qui prennent du temps ?? (j'ai prévu de les virer de toute façon)
    La public class Metabolisme n'est pas déclarer ou initialiser sur le thread UI (ni dans The_Master() d'ailleurs), ce temps de latence pourrait provenir de la ? J'ai 15 autre méthodes derrière, tous coder dans ce style et qui donc prennent aussi environ 1000ms a chaque fois...


    merci pour votre aide

  4. #4
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    vu que tu es fier, je vais dire qu'il y a du mieux
    enfin oui il y a du mieux

    par contre y a encore des trucs un peu hasardeux
    une variable de classe est utile quand on doit y avoir accès depuis plusieurs membres, ici seul un membre pourrait suffire, surtout qu'un thread une fois terminé ne peut être re starté
    => sur click thread th = new thread(...); th.Start();

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     if (bt1.Content.ToString() == "PAUSE")
    il est vrai que ca se fait sur des petits projets avec peu de code, mais tester le libellé d'un bouton pour connaitre un état c'est pas top, normalement on a une variable pour l'état (booléen ou enum/int si plus de 2 états)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Application.Current.Shutdown();
    c'est un peu violent mais pourquoi pas

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    deleguer maitre = The_Master;
    maitre();
    équivaut à
    tu n'as donc pas du comprendre ce qu'est un délégué, car ici il n'y a aucun interêt d'écrire ça
    un délégué c'est un pointeur vers une méthode via une variable
    ca permet d'appeler une méthode sans la connaitre (vu que c'est une variable on peut changer le pointeur pour dire que c'est une autre méthode) tant qu'elle a la signature prévue (signature = paramètres en nombre et en type)
    un délégué est souvent associé aux thread, mais parce qu'un dispatcher (sorte de thread) peut s'ajouter un appel à une méthode via un délégué, ce qui permet en gros de dire au thread principal qu'il doit appeler une méthode, ce qui permet de modifier l'interface
    et c'est un délégué car le framework n'a pas à connaitre ton code ^^
    mais bon de nos jours il y a plus simple que les délégués pour faire ça (classe action ou autre lambda expression, tu utilises d'ailleurs la classe action pour ton dispatcher.invoke)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    labdecision.Text = Decision;
    labcycle.Content = ncycle;
    labnyctemere.Content = Nyctemere;
    // ...
    j'avais du te faire un petit laïus sur mon précédent post pour militer pour une classe, voire du binding ^^

    là tu espères avoir assez de temps pour que l'interface se mette à jour, incompréhension aussi
    pour dire au thread principal de s'ajouter un truc à faire il y a soit .invoke, soit .begininvoke
    invoke est bloquant, à savoir tant que le code de l'action n'est pas fini de s'exécuter ton appli reste sur cette ligne
    donc attendre ne sert à rien, c'est déjà inclus et ca attendra le temps nécessaire
    .begininvoke n'est pas bloquant, ca ajoutes le truc à faire et ca continue sans attendre (vu que c'est 2 threads différents chacun fait sa vie et le thread principal fera le truc quand il aura le temps)


    les consoles.writeline mets les en commentaire pour voir si c'est plus rapide
    après 18s il faut voir si c'est le cumul de toutes les boucles
    et si la méthode est appelée plusieurs fois par boucle si c'est un cumul de tous les appels
    le chrono a une méthode Reset
    et je réitère si tu as un truc qui prend 200ms en console il doit prendre le même temps en wpf, l'affichage est normalement anecdotique (surtout avec begininvoke du coup)

    enfin tu as quelques pistes pour essayer de trouver le problème de perf (tu peux aussi mettre en commentaire le manualresetevent pour voir si c'est lui qui consomme du temps à vérifier l'état)
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

Discussions similaires

  1. [XL-2007] Boucle très lente pour supprimer des lignes
    Par Mycomer dans le forum Macros et VBA Excel
    Réponses: 25
    Dernier message: 21/01/2016, 16h34
  2. [XL-2010] exécution du code VBA Très Très lent dans un sens et rapide dans l'autre ?
    Par jfab66 dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 13/01/2016, 09h49
  3. Réponses: 3
    Dernier message: 31/01/2012, 07h45
  4. Boucle très lente [VBA]
    Par actuenligne dans le forum Macros et VBA Excel
    Réponses: 8
    Dernier message: 06/07/2011, 11h50
  5. Réponses: 2
    Dernier message: 04/03/2006, 10h47

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo