Partage
  • Partager sur Facebook
  • Partager sur Twitter

Thread, Pogress bar et class externe

Sujet résolu
    17 mai 2011 à 11:50:31

    Bonjour,

    Voici mon probleme :
    J'ai un formulaire dans lequelle j'entre deux adresse IP.
    Je click sur un bouton et le form execute une fonction(contenue dans une autre class IP) qui réalise un scan de la plage ip entrée.
    Tout cela marche parfaitement mais lorsqu'il faut scanner 50 adresses le form passe en not responding et si on change de programme on ne peut pas revenir sur le form avant la fin du scan, ce qui est embetant...

    voici ma fonction ScanIp
    public class IP
        {
            public delegate void ShowPogress(int val);
            public delegate void updtDGV(DataTable dt);
            
    public static void ScanIP(ShowPogress shProg, updtDGV updt, string ipStart,string ipEnd)
    {}
    


    Mon appel a la fonctin dans le form :
    IP.ScanIP(ProgressBarInc, UpdateDGV, tbIpStart.Text, tbIpEnd.Text);
    


    Je voudrais executer ce ScanIp dans un autre thread et que celui ci fasse avancer une progressBar(qui est deja en place avec ma methode actuelle qui incremente a l'aide d'un delegate passé en parametre au scan).

    UpdateDGv correspond a un delegate d'une fonction qui met a jour un datagridview a l'aide d'une datatble qui se remplit avec differente info dans mon scanIP

    Deja est ce que passé un delegate en parametre est correct pour pouvoir faire avancer ma progress bar dans mon scan ? :euh:

    Et comment est ce que je peux m'y prendre pour executer ce scan dans un autre thread pour ne pas bloque mon Form principal, si c'est la bonne solution ?

    J'ai essayer de mettre en place un Thread avec un ParameterizedThreadStart mais il lui faut des object en parametre et les delegate ne se cast pas en object, j'ai donc essayer la methode Invoke presente dans la tuto de Gretro(que j'ai lu) mais elle ne fonctionne apparemment que lorsque l'on est dans un Form.

    Bref je m'en remet a vous.

    PS: je precise que je ne connais pas bien du tout l'utilisation des thread ^^
    • Partager sur Facebook
    • Partager sur Twitter
      17 mai 2011 à 15:20:35

      Salut,

      Ce que tu veux faire me semble être un bon candidat pour utiliser un BackgroundWorker .

      Documentation:
      MSDN BackgroundWorker
      • Partager sur Facebook
      • Partager sur Twitter
        17 mai 2011 à 15:40:31

        Je me suis penché sur le sujet il y a quelque jours sans reel succes car je n'arrivais pas m'en servir, je vais donc retourner essayer ^^

        Est ce que les infos suivante sont juste svp:

        RunWorkerAsync()Demarre le worker.

        Ce qui declenche _DoWork.

        reportProgress() declenche ProgressChanged

        RunWorkerConpleted() est declencher a la fin de la fonction.

        il faut que je lance mon scanIp dans DoWork.


        Je pense avoir fait le tour des choses dont je voudrais etre sur d'avoir compris :)
        • Partager sur Facebook
        • Partager sur Twitter
          17 mai 2011 à 16:09:41

          J'ai mis à jour mon tutoriel sur les Threads en C#. Cependant, il est attente de validation. Je te recommande cependant d'aller faire un tour dans les bêta-test. J'explique le fonctionnement des threads et comment les utiliser. Je viens notamment d'ajouter la partie sur les BackgroundWorker.



          Ça devrait répondre à tes questions concernant les threads.
          • Partager sur Facebook
          • Partager sur Twitter
            18 mai 2011 à 8:57:12

            Merci gretro, j'ai lu ton tuto beta, il est vraiment au top.

            Par contre j'ai un petit soucis avec la meme erreur que tu presente dans le tuto, une variable d'un autre thread:

            private void bwProgress_DoWork(object sender, DoWorkEventArgs e)
                    {
                        IP.ScanIP(UpdateDGV, bwProgress.ReportProgress, tbIpStart.Text, tbIpEnd.Text);            
                    }
            
                    private void bwProgress_ProgressChanged(object sender, ProgressChangedEventArgs e)
                    {
                        //On fait avancer la ProgressBar.
                        pgbIpScan.Value = e.ProgressPercentage;
                        pgbIpScan.Update();
                    }
            
                    private void bwProgress_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
                    {
                        lblCurrentIP.Text = "Done!";
                    }
            
                    private void UpdateDGV(DataTable dt)
                    {
                        dgvDeskFound.DataSource = dt;
                        dgvDeskFound.Update();
                    }
            


            voila la partie de mon bgWorker, le probleme est avec le delegate et UpdateDGV, qui met a jour mon datagridview au fur et a mesure de l'avancer du scanIP

            Dans mon scan j'ai ceci :
            public delegate void ShowPogress(int val);
                    public delegate void updtDGV(DataTable dt);
            
            public static void ScanIP(updtDGV updt, ShowPogress shprog, string iPStart, string iPEnd)
            {
            ...
            updt(deskstops);
            shprog(progress);
            ...
            }
            


            Et donc au moment de faire mon UpdateDGV j'ai l'erreur de la variable dans un autre thread, pourtant les delegates servent bien a agir dans un autre thread non ? o_O
            Dans le tutoriel,Gretro, tu utilise tout dans la meme class/winform et je n'arrive pas a adapter pour mon ou j'utilise une methode externe a la classe de départ.
            Si j'utilise mal les diffrérents éléments ou qu'ils ne s'utilisent pas du tout comme ca merci de me le faire savoir :)
            • Partager sur Facebook
            • Partager sur Twitter
              18 mai 2011 à 15:55:43

              Je crois, à ce moment, que tu devrais utiliser une instruction Invoke pour résoudre le problème. Vois-tu, les composants Windows Forms ne sont pas pensés thread-safe. Cela fait en sorte que seul le thread qui les a créé (à savoir le thread principal) peut les modifier. Sinon, tu as seulement un accès en lecture seule. Regarde le second exemple de code dans la partie des Windows Forms, cela explique comment utiliser les Invoke.

              Donc en gros, dans ta méthode ScanIP, il te faudrait appeler Invoke((updtDGV)uptd); plutôt que simplement uptd();. Oh, j'allais oublier : Le Invoke ne semble pas apparaître quand tu n'es pas dans une classe qui hérite de Forms...
              • Partager sur Facebook
              • Partager sur Twitter
                19 mai 2011 à 9:23:45

                Invoke est en effet uniquement dans les classes qui herite de Form, je ne peux donc pas l'utiliser dans mon cas.
                Il me reste une solution c'est de passer les méthodes de ma classe IP dans mon formulaire mais ce n'est pas ce que je voulais au depart.
                • Partager sur Facebook
                • Partager sur Twitter
                  19 mai 2011 à 10:39:34

                  Citation : Hyti

                  pourtant les delegates servent bien a agir dans un autre thread non ? o_O


                  Non, absolument pas. Un delegate est une référence vers une méthode (éventuellement anonyme ou lambda). Appeler un delegate revient à exécuter immédiatement la méthode qu'il désigne.
                  Il est cependant possible d'effectuer l'appel du delegate dans un autre thread, ce qui aura pour effet d'exécuter la méthode dans ce thread. C'est ce qui se passe quand tu transmets ton delegate à la méthode Invoke des contrôles: le delegate sera appelé dans le thread principal dès que possible. Mais il faut bien comprendre que la notion de delegate n'est pas liée à la notion de thread.

                  private void bwProgress_DoWork(object sender, DoWorkEventArgs e)
                  {
                      IP.ScanIP(UpdateDGV, bwProgress.ReportProgress, tbIpStart.Text, tbIpEnd.Text);            
                  }
                  

                  Tu ne devrais idéalement pas manipuler tes contrôles à partir de ta méthode bwProgress_DoWork (même en lecture seule); il vaut mieux récupérer les valeurs qui t'intéressent avant qu'elle soit appelée. Utilise l'Argument du DoWorkEventArgs, c'est fait pour. :)

                  Citation : Hyti

                  Invoke est en effet uniquement dans les classes qui herite de Form, je ne peux donc pas l'utiliser dans mon cas.


                  Invoke est une méthode publique thread-safe de la classe Control. Rien ne t'empêche donc de faire ceci:

                  private void UpdateDGV(DataTable dt)
                  {
                      dgvDeskFound.Invoke((Action)delegate()
                      {
                          dgvDeskFound.DataSource = dt;
                          dgvDeskFound.Update();
                      });
                  }
                  

                  Ou alors tu gardes ta méthode UpdateDGV précédente et tu écris

                  private void bwProgress_DoWork(object sender, DoWorkEventArgs e)
                  {
                      IP.ScanIP(dt => dgvDeskFound.Invoke((Action<DataTable>)UpdateDGV, dt), bwProgress.ReportProgress, tbIpStart.Text, tbIpEnd.Text);            
                  }
                  

                  Au choix. ;)


                  Par ailleurs il est déconseillé de créer de nouveaux types de delegates. Les delegates génériques Action<> et Func<> suffisent généralement.
                  Définis donc plutot ta méthode ScanIP de cette manière:

                  public static void ScanIP(Action<DataTable> updt, Action<int> shprog, string iPStart, string iPEnd)
                  {
                      // ...
                  }
                  


                  Edit: petites corrections dûes au fait que la (vieille) méthode Invoke des WinForms prend un Delegate en argument (au lieu d'un delegate Action), ce qui impose le casting explicite. Dommage, et heureusement WPF gère ça mieux :)
                  • Partager sur Facebook
                  • Partager sur Twitter
                    19 mai 2011 à 14:49:58

                    Eh bien je vais aller testé ca, merci pour ta réponse :)
                    Action<> equivaut a :
                    public delegate void bidule()
                    scanIP(bidule bid...)

                    Action<> remplacant Bidule ()
                    si j'ai bien suivi ?

                    dt => dgvDeskFound.Invoke(UpdateDGV, dt)
                    envoi la reference de update a scanip ? je ne comprends pas tres bien cette utilisation enfin surtout le dt => . :o

                    Pour l'autre methode :
                    dgvDeskFound.Invoke(delegate()
                        {
                            dgvDeskFound.DataSource = dt;
                            dgvDeskFound.Update();
                        });
                    


                    j'ai un message d'erreur :
                    Cannot convert anonymous method to type 'System.Delegate' because it is not a delegate type :(

                    Regler par ton dernier Edit =)


                    Sa marche du feux de dieu! Merci beaucoup. :D
                    Si je peux avoir les explications quand meme ? pour etre sur d'avoir bien tout compris ce serait TipTop :soleil:
                    • Partager sur Facebook
                    • Partager sur Twitter
                      19 mai 2011 à 16:29:29

                      Action est un delegate "classique" dont la définition est la suivante:

                      public delegate void Action();
                      


                      Il est défini dans le namespace System. De fait, dès que tu veux référencer des méthodes de type void et sans paramètres, tu peux utiliser le type de delegate Action au lieu d'en définir un nouveau.
                      Il existe également des variantes génériques, au cas où tu voudrais référencer des méthodes ayant des arguments:

                      public delegate void Action<TArg>(TArg arg1);
                      public delegate void Action<TArg1, TArg2>(TArg1 arg1, TArg2 arg2);
                      ...
                      


                      Donc dans ton cas par exemple, au lieu de définir des nouveaux types de delegate comme ceci:

                      public delegate void ShowPogress(int val);
                      public delegate void updtDGV(DataTable dt);
                      


                      Il suffit d'utiliser les types Action<int> et Action<DataTable> à la place. :)

                      L'équivalent d'Action pour les méthodes qui possèdent un type de retour différent de void est Func:

                      public delegate TReturn Func<TReturn>();
                      public delegate TReturn Func<TArg, TReturn>(TArg arg);
                      public delegate TReturn Func<TArg1, TArg2, TReturn>(TArg1 arg1, TArg2 arg2);
                      ...
                      

                      En pratique avec Action et Func on couvre 99% des cas d'utilisation des delegates. Il reste possible de spécifier ses propres types en cas de besoin, mais c'est à éviter. A noter qu'il existe quelques spécialisations de Action et de Func, par exemple Predicate qui s'utilise comme Action mais pour les méthodes dont le type de retour est bool plutôt que void.


                      gretro ne parle pas des méthodes anonymes dans son tuto, et je trouve ça un peu dommage.
                      En fait on peut aussi utiliser des delegates pour référencer des méthodes créées "à la volée".

                      Par exemple je peux écrire:

                      Func<int, int> ajouter10 = delegate(int val)
                      {
                          return val + 10;
                      };
                      


                      Je crée ainsi une méthode anonyme qui prend un int en argument et qui renvoie un int. Je peux l'appeler ensuite comme n'importe quelle méthode grâce au delegate ajouter10. Au moment de créer ma méthode je peux l'assigner à un delegate ou la passer directement en paramètre d'une autre méthode, comme je l'ai fait plus haut avec la méthode Invoke:

                      dgvDeskFound.Invoke((Action)delegate()
                      {
                          dgvDeskFound.DataSource = dt;
                          dgvDeskFound.Update();
                      });
                      

                      (Ici le casting est nécessaire parce que Invoke s'attend à recevoir un objet System.Delegate, et la conversion d'une méthode anonyme en Delegate n'est hélas pas automatique.)

                      A noter qu'on peut utiliser des variables "externes" à la méthode anonyme sans nécessairement les déclarer en paramètres. Par exemple ici j'utilise le paramètre "dt" de la méthode UpdateDGV ; et bien que cette méthode sera terminée au moment de l'exécution de ma méthode anonyme dans le thread principal, je pourrai toujours utiliser l'objet dt transmis en paramètre. C'est assez puissant, mais il faut être bien conscient de ce que ça implique: ici dt est une référence vers l'objet DataTable transmis (et pas une copie), donc si je modifie ce même objet d'une façon ou d'une autre en dehors de la fonction ça risque de donner des résultats bizarres :-°


                      Pour ce qui concerne la notation "dt =>" qui t'intrigue, il s'agit en fait d'une expression lambda. Ma méthode anonyme ajouter10 pourrait s'écrire sous la forme raccourcie suivante:

                      Func<int, int> ajouter10 = val => val + 10;
                      


                      Ce qui se trouve avant le "=>" est la liste des paramètres (sans leur type, et entre parenthèses en général sauf s'il n'y en a qu'un), et ce qui suit est la valeur de retour (sans return), ou un bloc d'instructions entouré d'accolades s'il y a plus d'une instruction à exécuter.
                      On peut bien sûr transmettre une expression lambda en paramètre d'une méthode, et ça se fait extrêmement souvent avec LINQ entre autres. Par exemple:

                      List<int> values = new List<int>() { 4, 56, 54, 8, 23, 79 2 };
                      List<int> squaresOfvaluesAbove10 = values.Where(v => v > 10).Select(v => v*v).ToList();
                      


                      Donc ce que j'ai écrit plus haut est une notation abrégée équivalente à

                      private void bwProgress_DoWork(object sender, DoWorkEventArgs e)
                      {
                          IP.ScanIP(delegate(DataTable dt)
                          {
                              dgvDeskFound.Invoke((Action<DataTable>)UpdateDGV, dt);
                          }, 
                          bwProgress.ReportProgress, tbIpStart.Text, tbIpEnd.Text);            
                      }
                      


                      J'espère que ça paraitra plus clair comme ça :)
                      • Partager sur Facebook
                      • Partager sur Twitter
                        20 mai 2011 à 8:59:48

                        :magicien::magicien::magicien::magicien:
                        Super, merci beaucoup j'ai compris!
                        C'est vrai que ces aspects de .net semble vraiment utiles, un tuto serait cool c'est sur :-°

                        Je vais garder tes explications dans un coin de favoris en tout cas :p

                        Encore merci pour toutes vos reponses!
                        • Partager sur Facebook
                        • Partager sur Twitter

                        Thread, Pogress bar et class externe

                        × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                        × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                        • Editeur
                        • Markdown