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

C# Discussion :

Task dans une boucle [Débutant]


Sujet :

C#

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Débutant
    Inscrit en
    Février 2007
    Messages
    134
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Débutant
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Février 2007
    Messages : 134
    Par défaut Task dans une boucle
    Bonsoir à tous,

    je rencontre un probleme avec le code ci-dessous.

    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
    		private async void test(CancellationToken token)
    		{
    			string tmp = dtgViewDouble.Rows[0].Cells[1].Value.ToString();
     
    			progressBar1.Maximum = dtgViewDouble.Rows.Count;
     
    			for(int i = 1; i<dtgViewDouble.Rows.Count; i++) //TODO: tres tres lents si beaucoup d'élement
    			{
     
    				//Task.Run(() =>{		
     
    				  if(dtgViewDouble.Rows[i].Cells[1].Value.ToString() == tmp)
    				{
    					dtgViewDouble.Rows[i].Selected = true;
    				}
    				else
    				{
    					tmp = dtgViewDouble.Rows[i].Cells[1].Value.ToString();
    				}
    				UpdateProgressBar();
     
    				//}, token);
     
    				//Task.WaitAll();
     
    			}
    		}

    Ce code fonctionne parfaitement bien à l'exception qu'il freeze l'interface graphique du programme lors de traitement long. J'ai donc voulu placer une task (en commentaire dans le code) avec une possibilité d'annulation pour créer une function asynchrone avec mise à jour d'une progressbar.

    Le programme compile bien mais ne fait pas le travail demandé dans la task.

    Pourriez-vous donc m'aider à comprendre le problème.

  2. #2
    Membre chevronné
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Août 2008
    Messages
    381
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Août 2008
    Messages : 381
    Par défaut
    Un début de solution :
    Task.Run retourne une task, c'est elle que tu dois attendre, mais avec Task.WhenAll (car WaitAll gèle le thread).

    var t = Task.Run....
    Task.WhenAll(t)

  3. #3
    Expert confirmé

    Avatar de François DORIN
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Juillet 2016
    Messages
    2 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2016
    Messages : 2 761
    Billets dans le blog
    21
    Par défaut
    Bonjour,

    Ton code ne marche pas, car la tâche que tu lances génère une exception (InvalidOperationException, car tu essaies d'accéder à des éléments de l'interface graphique en dehors du thread graphique).

    En général, pour ce genre de choses, il y a deux possibilités :
    • Utiliser async/await ;
    • Utiliser le patron d'invocation.


    Ces approches permettent de réaliser de long calcul sans bloquer l'interface graphique.

    Maintenant, je pense que le problème se situe ailleurs. En effet, tu n'as pas de calcul long, tu as juste beaucoup d'éléments graphique (les lignes de ton datagridview) à mettre à jour lorsque tu les sélectionnes.

    La seconde méthode est inutilisable, car tu accèdes à la fois à des éléments graphiques pour mettre à jour des données mais aussi pour en récupérer.

    La première ne peut pas fonctionner ici non plus, comme tu n'as pas de longue tâche à attendre (pas de possibilité de faire un await).

    Malheureusement, ici, je ne vois pas de solution simple à mettre en oeuvre. La seule solution que je vois qui permettrait de ne pas bloquer complètement l'interface graphique serait de traiter tes lignes non pas d'un coup, mais par lot (par ex. lot de 20) et de créer un petit délai entre chaque lot pour laisser le temps au thread graphique de se mettre à jour.

    Code C# : 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
    private async void test(CancellationToken token)
    {
       string tmp = dtgViewDouble.Rows[0].Cells[1].Value.ToString();
       progressBar1.Maximum = dtgViewDouble.Rows.Count;
     
       for(int i = 1; i<dtgViewDouble.Rows.Count; i++) //TODO: tres tres lents si beaucoup d'élement
       {
          if(dtgViewDouble.Rows[i].Cells[1].Value.ToString() == tmp)
          {
             dtgViewDouble.Rows[i].Selected = true;
          }
          else
          {
             tmp = dtgViewDouble.Rows[i].Cells[1].Value.ToString();
          }
     
          // Tous les 20 éléments, on met à jour la barre de progression et on libère le thread graphique pendant 50ms
          // afin qu'il puisse faire des mises à jour.
          if (i % 20 == 0) 
          {
             // Inutile de faire appel à UpdateProgressBar à chaque itération. Cela ne fait que surcharger le thread graphique pour rien.
             // Autant faire un appel juste avant de redonner la main.
             UpdateProgressBar(); 
             await Task.Delay(50, token);
             if (token.IsCancellationRequested)
             {
                return;
             }
          }
       }
    }

    Les valeurs 20 (taille du lot) et 50 (délai d'attente) sont totalement arbitraire. C'est pour l'exemple. Et sans doute à adapter. Le 50 peut sans doute être descendu à 1 pour ne pas attendre 50ms entre chaque lot. A tester

  4. #4
    Membre confirmé
    Profil pro
    Débutant
    Inscrit en
    Février 2007
    Messages
    134
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Débutant
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Février 2007
    Messages : 134
    Par défaut
    @François DORIN

    Je vais être plus complet avec mon code. La mise à jour de la progressbar se fait de la manière suivante :
    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
     
    delegate void Progress();
     
    private void UpdateProgressBar();()
    {			
    	if (this.progressBar1.InvokeRequired)
    	{
    		Progress d = new Progress(Progress);
    		this.Invoke(d, new object[] {});
    	}
    	else
    	{
    		progressBar1.Value += 1; 
    	}
    }
    Donc en principe la mise à jour de l'interface se fait bien dans le thread principal.

    Et effectivement le problème vient bien du nombre d'élements du datagridview qui sont nombreux et ralentissent le traitement. Maintenant je ne suis pas convaincu d'un traitement par paquet car il me semble que ca fait un peu bricolage. Existe-til donc une autre façon éventuelle pour faire cette selection de ligne?

  5. #5
    Expert confirmé

    Avatar de François DORIN
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Juillet 2016
    Messages
    2 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2016
    Messages : 2 761
    Billets dans le blog
    21
    Par défaut
    Tu utilises bien le pattern d'invocation pour mettre à jour la barre de progression. Mais pas pour l'accès au DataGridView (cf. par exemple la sélection d'une ligne dtgViewDouble.Rows[i].Selected = true;).

    Et effectivement, cela fait un peu bricolage, mais je ne vois pas de méthode simple pour y arriver. Au delà, il faudrait pouvoir bloquer la mise à jour le temps de faire toutes les modifications puis de les réactiver. Un peu à la manière des SuspendLayout et ResumeLayout pour les conteneurs (lorsqu'on ajoute plusieurs enfants, modifie plusieurs propriétés liées au positionnement, etc...). Il doit être possible d'y arriver en dérivant du DataGridView.

    A voir aussi s'il n'y a pas des handlers associés à la sélection d'une ligne, qui pourraient ralentir encore la sélection.

    Et à ma connaissance, il n'y a pas de manière de sélectionner plusieurs lignes à l'aide d'une seule méthode.

  6. #6
    Membre confirmé
    Profil pro
    Débutant
    Inscrit en
    Février 2007
    Messages
    134
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Débutant
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Février 2007
    Messages : 134
    Par défaut
    @François DORIN

    1. Ok j'ai compris le problème de l'accès entre thread de la mise à jour du datagridview.
    2. j'ai essayé avec des datagridview.SuspendLayout et ResumeLayout mais le problème reste le même
    3. Il y a bien des handlers qui sont lancé lors du changement de selection puisque la selection change... Ce qui ralentis effectivement encore le processus.

    J'ai par contre pensé à une autre solution mais sur laquelle je coince.

    Je remplis mon datagridview de la manière suivante:

    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
    		private void SetDatasource(List<Mark> lst)
    		{
    			if(lst == null) return;
     
    			int index;
     
    			dtgViewDouble.Columns.Clear();
    			dtgViewDouble.AutoGenerateColumns = false;		
     
    			index = dtgViewDouble.Columns.Add("Titre", "Titre");
    			dtgViewDouble.Columns[index].DataPropertyName = "Titre";
     
    			index = dtgViewDouble.Columns.Add("Href", "Href");
    			dtgViewDouble.Columns[index].DataPropertyName = "Href";
     
                           //Placer de manière automatique la valeur de selection de la ligne à une propriété de mon object
                           // exemple : dtgViewDouble.row[comment faire l'index].selected = (DataPropertyName = "Selection");
     
    			dtgViewDouble.DataSource = lst;
    		}
    Comment pourrais envisager un code permettant de faire ce qui est placé en commentaire?

    Merci

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [langage] incrementation de variable dans une boucle
    Par mimilou dans le forum Langage
    Réponses: 15
    Dernier message: 16/04/2004, 13h23
  2. Problème avec TNMSMTP dans une boucle.
    Par Orgied dans le forum Web & réseau
    Réponses: 3
    Dernier message: 07/04/2004, 10h19
  3. swf dans une boucle asp
    Par Chucky69 dans le forum Flash
    Réponses: 11
    Dernier message: 10/02/2004, 17h07
  4. [Vb.net] Indexé un objet crée dans une boucle
    Par picpic dans le forum Windows Forms
    Réponses: 10
    Dernier message: 17/12/2003, 14h37
  5. Pause dans une boucle
    Par HT dans le forum Langage
    Réponses: 4
    Dernier message: 03/06/2003, 08h52

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