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#

  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

  7. #7
    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
    Pour le point 2 c'est normal. SuspendLayout et ResumeLayout sont fait pour les conteneurs (comme les Panel). C'était juste un exemple.

    Pour le point 3 il faudrait faire un test sans les handlers, pour voir si le délai provient de ces handlers ou de la mise à jour du DataGridView.

    A ma connaissance, il n'est pas possible de faire ce que tu souhaites faire dans tes commentaires. Pour sélectionner de multiple ligne, il faut itérer sur les lignes et les sélectionner une à une.

  8. #8
    Membre Expert

    Homme Profil pro
    Développeur .NET
    Inscrit en
    Novembre 2010
    Messages
    2 067
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Novembre 2010
    Messages : 2 067
    Par défaut
    Pourquoi ne pas utiliser notre célèbre ami le BackgroundWorker star en son temps pour les traitements longs sur Windows Forms:

    http://webman.developpez.com/article...ckroundworker/
    https://www.developpez.net/forums/d1...hangement-gui/

  9. #9
    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
    Citation Envoyé par François DORIN Voir le message
    Pour le point 3 il faudrait faire un test sans les handlers, pour voir si le délai provient de ces handlers ou de la mise à jour du DataGridView.
    Après des test il s'agissait bien des handlers qui ralentissait l'opération. Mais du coup je rencontre un dernier problème pour résoudre définitivement la discussion:

    Suppression des handlers
    Selection tels que je le veux et traitement...
    Remise des handlers

    Comment je peux déclencher les événements necessaire que je souhaite après la remise des Handlers???

    Merci pour l'aide.

  10. #10
    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
    Citation Envoyé par youtpout978 Voir le message
    Pourquoi ne pas utiliser notre célèbre ami le BackgroundWorker star en son temps pour les traitements longs sur Windows Forms
    Ce n'est pas possible ici. Ce n'est pas un traitement long, c'est une multitude de traitements courts. Mais comme ils sont nombreux, ça prend du temps. Et comme chaque traitement accède à des éléments graphiques, cela ne sert à rien ici.

    Citation Envoyé par agparchitecture
    Après des test il s'agissait bien des handlers qui ralentissait l'opération.

    Comment je peux déclencher les événements necessaire que je souhaite après la remise des Handlers???
    Pas de solution miracle malheureusement. Et cela dépend du fonctionnement de tes handlers. Est-ce qu'appeler 50 fois le handler ou n'effectuer que le dernier appel revient au même ?
    Si oui, une idée serait de faire une classe dérivant de DataGridView afin de pouvoir écrire une méthode publique encapsulant un appel à une méthode style OnSelectionChanged (qui est protected, d'où le besoin d'y accéder dans une classe dérivée) afin de pouvoir déclencher manuellement l'événement une fois que les handlers sont remis en place...

  11. #11
    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
    Citation Envoyé par François DORIN Voir le message
    Est-ce qu'appeler 50 fois le handler ou n'effectuer que le dernier appel revient au même ?
    On peux appeler uniquement le dernier appel puisqu'il s'agit seulement de mettre un label indiquant le nombre de lignes sélectionnés (Celui-ci n'a pas forcément besoins d'être mis à jours pour chaque sélection de ligne du datagridview)

    je vais essayer en créant un control héritant du datagridview pour voir ce que je peux en tirer. Faut juste me laisser un peu de temps avant de revenir pour les résultats.

    C'est bon ça marche très bien maintenant.

    Merci pour l'aide

+ 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