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 :

parrallel.foreach avec datagridview [Débutant]


Sujet :

C#

  1. #1
    Membre régulier
    Profil pro
    Débutant
    Inscrit en
    Février 2007
    Messages
    127
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : Belgique

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

    Informations forums :
    Inscription : Février 2007
    Messages : 127
    Points : 87
    Points
    87
    Par défaut parrallel.foreach avec datagridview
    Bonsoir à tous,

    J'ai dans une interface winform un datagridview. Lorsque je clique sur un bouton, je voudrais vérifier les éléments sélectionnés Mon code est :

    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
     
     
    private List<Mark> Test = new List<Mark>();
     
    private void button2_Click(object sender, EventArgs e)
    		{
    			List<Mark> lst = new List<Mark>();
    			Mark m = new Mark();
    			foreach (DataGridViewRow row in dataGridView1.SelectedRows)
    			{
    				m = row.DataBoundItem as Mark;			
    				lst.Add(m);
    			}
     
     
    			Parallel.ForEach(lst, new ParallelOptions { MaxDegreeOfParallelism = 4 }, cm => 
    			                 {
    			                 	cm.Titre = MarkNet.Tools.HtmlTools.gettitle(m.Href);
    			                 	if (cm.Titre != "")
    			                 	{
    			                 		lock (Test)
    			                 		{
    			                 			Mark obj = Test.FirstOrDefault( x=> x.UId == m.UId);
    			                 			if (obj != null) obj.Titre = m.Titre;
    			                 			dataGridView1.Update();
    			                 		}
    			                 	}
    			                 }
    			                );
    }
    Pourtant le seul message que j'obtient à l’exécution est : Une ou plusieurs erreurs se sont produites.
    Rien dans le compilateur ni dans le déboguage.

    Pourriez-vous m'éclairer? Merci

  2. #2
    Expert éminent sénior

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

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

    Informations forums :
    Inscription : Juillet 2016
    Messages : 2 757
    Points : 10 697
    Points
    10 697
    Billets dans le blog
    21
    Par défaut
    Bonjour,

    Le problème vient très certainement du fait que seul le thread graphique est autorisé à accéder aux éléments graphiques (par exemple, en faisant dataGridView1.Update();).

    Maintenant, Parallel.ForEach utilises plusieurs thread. Dans ton code, jusqu'à 4. Et le code dans la boucle accède à des éléments graphiques. Résultat : InvalidOperationException.

    Solutions :
    • ou bien enlever le Parallel.ForEach et le remplacer par une boucle for classique ;
    • ou bien enlever tous les accès aux éléments graphiques de la boucle.


    Au passage, l'usage de l'instruction lock est généralement à éviter au sein du thread graphique.
    François DORIN
    Consultant informatique : conception, modélisation, développement (C#/.Net et SQL Server)
    Site internet | Profils Viadéo & LinkedIn
    ---------
    Page de cours : fdorin.developpez.com
    ---------
    N'oubliez pas de consulter la FAQ C# ainsi que les cours et tutoriels

  3. #3
    Membre confirmé
    Avatar de nouanda
    Homme Profil pro
    Hobbyist
    Inscrit en
    Mai 2002
    Messages
    246
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Australie

    Informations professionnelles :
    Activité : Hobbyist

    Informations forums :
    Inscription : Mai 2002
    Messages : 246
    Points : 627
    Points
    627
    Par défaut
    Il est possible pour un thread d'accéder aux éléments graphiques de l'UI (même si c'est déconseillé...). Il faut pour cela créer une méthode dédiée, et un callback.

    Par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
            private void UpdateDgv()
            {
                if (this.dataGridView1.InvokeRequired)
                {
                    UpdateDgvCallback d = new UpdateDgvCallback(UpdateDgv);
                    this.Invoke(d, new object[] { });
                }
                else
                {
                    this.dataGridView1.Update();
                }
            }
    Déclarer le callback ainsi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    delegate void UpdateDgvCallback();
    Tu peux ainsi, dans tes thread, remplacer dataGridView1.Update(); par UpdateDgv();

    Edit: regarde ici http://stackoverflow.com/questions/6...er-thread-in-c pour des méthodes plus générales
    " Entre le Savoir et le Pouvoir, il y a le Vouloir "

    Desole pour les accents, je suis en QWERTY...

  4. #4
    Expert éminent sénior

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

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

    Informations forums :
    Inscription : Juillet 2016
    Messages : 2 757
    Points : 10 697
    Points
    10 697
    Billets dans le blog
    21
    Par défaut
    Citation Envoyé par nouanda Voir le message
    Il est possible pour un thread d'accéder aux éléments graphiques de l'UI (même si c'est déconseillé...). Il faut pour cela créer une méthode dédiée, et un callback.
    Ce n'est pas du tout déconseillé. C'est la méthode à suivre dès lors qu'un thread autre que le thread graphique souhaite mettre à jour l'UI.

    Par contre, vu l'utilisation de lock dans son code, je déconseillerai fortement cette approche, vu les risques d'obtenir des interblocages.
    François DORIN
    Consultant informatique : conception, modélisation, développement (C#/.Net et SQL Server)
    Site internet | Profils Viadéo & LinkedIn
    ---------
    Page de cours : fdorin.developpez.com
    ---------
    N'oubliez pas de consulter la FAQ C# ainsi que les cours et tutoriels

  5. #5
    Membre régulier
    Profil pro
    Débutant
    Inscrit en
    Février 2007
    Messages
    127
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : Belgique

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

    Informations forums :
    Inscription : Février 2007
    Messages : 127
    Points : 87
    Points
    87
    Par défaut
    Bon ok je ne m'en sort pas. Donc je vais poser la question autrement.

    J'ai les procédures suivantes qui fonctionne parfaitement.
    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
     
    private List<Mark> lstMark = new List<Mark>();
     
    private void button2_Click(object sender, EventArgs e)
    {
    	Mark m = new Mark();
    	foreach (DataGridViewRow row in dataGridView1.SelectedRows)
    	{
    		m = row.DataBoundItem as Mark;			
    		m.Titre = MarkNet.Tools.HtmlTools.gettitle(m.Href);
    		if(m.Titre != "")
                    {
                    	Mark obj = Test.FirstOrDefault( x=> x.UId == m.UId);
    			if (obj != null) obj.Titre = m.Titre;
    			dataGridView1.Update();
                    }
    	}
    }
    Comment puis-je convertir cette procédure afin de faire la boucle sur plusieurs ligne en même temps?

    Merci pour votre aide.

  6. #6
    Expert éminent sénior

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

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

    Informations forums :
    Inscription : Juillet 2016
    Messages : 2 757
    Points : 10 697
    Points
    10 697
    Billets dans le blog
    21
    Par défaut
    comment est définie la variable ou propriété Test ?
    François DORIN
    Consultant informatique : conception, modélisation, développement (C#/.Net et SQL Server)
    Site internet | Profils Viadéo & LinkedIn
    ---------
    Page de cours : fdorin.developpez.com
    ---------
    N'oubliez pas de consulter la FAQ C# ainsi que les cours et tutoriels

  7. #7
    Membre régulier
    Profil pro
    Débutant
    Inscrit en
    Février 2007
    Messages
    127
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : Belgique

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

    Informations forums :
    Inscription : Février 2007
    Messages : 127
    Points : 87
    Points
    87
    Par défaut
    Citation Envoyé par François DORIN Voir le message
    comment est définie la variable ou propriété Test ?
    En fat c'est mon lstMark dans mon dernier post donc c'st ma list<Mark> que j'affiche dans mon datagridview par un datasource.

  8. #8
    Expert éminent sénior

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

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

    Informations forums :
    Inscription : Juillet 2016
    Messages : 2 757
    Points : 10 697
    Points
    10 697
    Billets dans le blog
    21
    Par défaut
    Voici un exemple de code. Attention, je ne l'ai pas testé, donc il peut y avoir des coquilles !

    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
    32
    33
    34
    35
    36
    37
    private List<Mark> lstMark = new List<Mark>();
     
    private void button2_Click(object sender, EventArgs e)
    {
    	foreach (DataGridViewRow row in dataGridView1.SelectedRows)
    	{
    		Mark m = row.DataBoundItem as Mark;			
    		string href = m.Href;
     
    		Task.Run(() => {
    			string titre = MarkNet.Tools.HtmlTools.gettitle(href);		
    			UpdateMark(m, titre);
    		});
    	}
    }
     
    delegate void UpdateDgvCallback(Mark m, string titre);
     
    private void UpdateMark(Mark m, string titre)
    {
    	if (this.dataGridView1.InvokeRequired)
    	{
    		UpdateDgvCallback d = new UpdateDgvCallback(UpdateDgv);
    		this.Invoke(d, new object[] {m, titre});
    	}
    	else
    	{
    		m.Titre = titre;
    		if (m.Titre != "")
    		{			
    			Mark obj = Test.FirstOrDefault(x=> x.UId == m.UId);
    			if (obj != null) obj.Titre = m.Titre;
    			dataGridView1.Update();		
    		}
     
    	}
    }

    Voici en gros l'idée. J'ai supprimé le lock. Mais pour éviter qu'il y ait tout soucis, je me suis assuré que tous les accès aux instances de Mark se fassent dans le thread graphique. C'est pour cela que j'ai défini la méthode UpdateMark qui prend comme paramètre le Mark à modifier et le titre à utiliser. Cette méthode s'occupe ensuite de s'assurer que tout le traitement se fait dans le thread graphique (surtout, si tu as d'autres threads qui peuvent accéder à tes instances de Mark, alors le code ci-dessus n'est pas valide).

    J'ai isolé la partie chronophage (le MarkNet.Tools.HtmlTools.gettitle) pour qu'elle soit exécuté dans des threads différents. J'utilise pour cela le pool de threads. Il y a une différence avec le code que tu as fourni, c'est que la, il n'y a pas de contrôle sur le nombre de traitements simultanés.

    Si tu as vraiment besoin de cela, alors, tu peux utiliser Parallel.ForEach. Pour cela, il faut :
    • créer une liste de Mark ;
    • lancer une tâche (par exemple, via Task.Run) qui contiendra le Parallel.ForEach;
    • mettre le code actuellement dans Task.Run dans le Parallel.ForEach.
    François DORIN
    Consultant informatique : conception, modélisation, développement (C#/.Net et SQL Server)
    Site internet | Profils Viadéo & LinkedIn
    ---------
    Page de cours : fdorin.developpez.com
    ---------
    N'oubliez pas de consulter la FAQ C# ainsi que les cours et tutoriels

  9. #9
    Membre expert
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Octobre 2013
    Messages
    1 563
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2013
    Messages : 1 563
    Points : 3 404
    Points
    3 404
    Par défaut
    Citation Envoyé par nouanda Voir le message
    Il est possible pour un thread d'accéder aux éléments graphiques de l'UI (même si c'est déconseillé...). Il faut pour cela créer une méthode dédiée, et un callback.

    Pour compléter ce qu'a dit @François DORIN, le this.Invoke(CallBack) est réalisé sur le thread principale de "this" et donc, le cas échéant, le thread principale de l'application (qui gère l'UI). Donc tu n'accèdes pas réellement aux composants graphiques dans un autres thread, mais bien dans celui qui gère ton UI.

  10. #10
    Membre régulier
    Profil pro
    Débutant
    Inscrit en
    Février 2007
    Messages
    127
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : Belgique

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

    Informations forums :
    Inscription : Février 2007
    Messages : 127
    Points : 87
    Points
    87
    Par défaut
    Merci ça fonctionne parfaitement

    juste une coquille
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    UpdateDgvCallback d = new UpdateDgvCallback(UpdateDgv); à remplacer par UpdateDgvCallback d = new UpdateDgvCallback(UpdateMark);
    Je mets en résolus et merci du coup de main

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

Discussions similaires

  1. Réponses: 5
    Dernier message: 29/03/2007, 00h08
  2. Foreach avec <tr>
    Par chipie189 dans le forum Langage
    Réponses: 2
    Dernier message: 16/01/2007, 01h44
  3. [c#] Travail avec datagridview.
    Par penchu dans le forum Windows Forms
    Réponses: 5
    Dernier message: 21/12/2006, 17h59
  4. Utilisation de foreach avec les sessions sous PHP5
    Par frochard dans le forum Langage
    Réponses: 1
    Dernier message: 11/11/2006, 01h14
  5. [Tableaux] Problème de foreach avec des checkboxes
    Par nanor21 dans le forum Langage
    Réponses: 10
    Dernier message: 15/05/2006, 01h04

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