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 :

C# Thread et performances


Sujet :

C#

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre habitué
    Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    10
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2007
    Messages : 10
    Par défaut C# Thread et performances
    Bonjour,

    Je commence à développer une application utilisant plusieurs Thread. Pour le moment l'appli est assez simple il s’agit de décoder des enregistrement dans un fichier binaire, et de les stocker dans un Dataset. Pour faire un peu plus userfreindly j’ai ajouté un ProgressBar. La visiblement si l’on veux que le ProgressBar progresse il faut maître le corps du traitement non plus dans le Thread principal (ouvert par le Form) mais dans un Thread secondaire. Et c’est la que je me heurte à un problème de performance, un code pratiquement identique (a l’exception de la déclaration et de l’ouverture du Thread) me fait passer d’un temps de traitement de 7 seconde à plus de 22…

    Y aurait il donc une façon de faire particulière pour accélérer la chose ?

    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
     
            # region Reading File
     
            private void reading(string fileName)
            { 
                storeDs.Tables["Records"].Clear();
                dataGridView.DataSource = null;
     
                ewsd = new EwsdReader();
                ewsd.open(fileName);
                startProgression();
                RecordReader rec;
                while ((rec =ewsd.readNextRecord())!= null)
                {
                    DataRow newRow = storeDs.Tables["Records"].NewRow();
                    newRow["Index"] = storeDs.Tables["Records"].Rows.Count + 1;
                    newRow["Address"] = rec.address + 1;
                    newRow["Type"] = rec.Name;
     
                    // read inner data
                    PackageReader pkg;
                    rec.read();
                    if ((pkg = rec.packages.identify(100)) != null) newRow["DateTime"] = pkg.fields[1].Value;
                    if ((pkg = rec.packages.identify(110)) != null) newRow["ConnectId"] = pkg.fields[2].Value;
     
                    // sotre data in dataset
                    storeDs.Tables["Records"].Rows.Add(newRow);
                }
                stopReading();
            }
     
            private void startReading(string fileName)
            {
                storeDs.Tables["Records"].Clear();
                dataGridView.DataSource = null;
     
                ewsd = new EwsdReader();
                ewsd.open(fileName);
                startTime = System.DateTime.Now;
                readingTread = new Thread(new ThreadStart(readDataLoop));
                readingTread.Name = "ReadingFile";
                readingTread.IsBackground = true;
                readingTread.Priority = ThreadPriority.BelowNormal;
                readingTread.Start();
                startProgression();
            }
            private void readDataLoop()
            {
                while (Thread.CurrentThread.IsAlive)
                {
                    this.Invoke(new process(storeData));
                }
            }
     
            private void storeData()
            {
                RecordReader rec=ewsd.readNextRecord();
                if (rec != null)// End of file
                {
                    // crearte new row in dataset
                    DataRow newRow = storeDs.Tables["Records"].NewRow();
                    newRow["Index"] = storeDs.Tables["Records"].Rows.Count + 1;
                    newRow["Address"] = rec.address + 1;
                    newRow["Type"] = rec.Name;
     
                    // read inner data
                    PackageReader pkg;
                    rec.read();
                    if ((pkg = rec.packages.identify(100)) != null) newRow["DateTime"] = pkg.fields[1].Value;
                    if ((pkg = rec.packages.identify(110)) != null) newRow["ConnectId"] = pkg.fields[2].Value;
     
                    // sotre data in dataset
                    storeDs.Tables["Records"].Rows.Add(newRow);
                }
                else
                {
                    readingTread.Abort();
                    stopReading();
                }
            }
            private void stopReading()
            {
                DataView dv = new DataView(storeDs.Records);
                dataGridView.DataSource = dv;
                dataGridView.Refresh();
                stopProgression();
            }
            #endregion
    Merci d'avance.

  2. #2
    Membre émérite
    Profil pro
    Inscrit en
    Août 2003
    Messages
    835
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2003
    Messages : 835
    Par défaut
    Salut,

    Je suis pas sur de bien comprendre ce que tu fais exactement, je me pose des questions sur

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    while (Thread.CurrentThread.IsAlive)
                {
                    this.Invoke(new process(storeData));
                }
    Qu'est ce que c'est que cette clause dans le while ?
    Ensuite tu fais un this.invoke qui va exécuter ton délégué dans ton thread principal, à quoi bon lancer un thread alors ?

  3. #3
    Membre habitué
    Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    10
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2007
    Messages : 10
    Par défaut
    Bonjour,

    L'Invoke permet d'utiliser une méthode Delegate (process), dont le but est de partager l'accès aux objets créés par le Thread principal dans le Thread secondaire (ce qui dans le cas on l'on utilise pas de Delegate lève une exception).

    Mon objet EWSD, ainsi que le Dataset étant créé par le Thread principale, c'est d'après ce que j'ai pu lire la façon la plus propre de partager les objets (Framework 2.0 ou sup).

  4. #4
    Membre émérite
    Profil pro
    Inscrit en
    Août 2003
    Messages
    835
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2003
    Messages : 835
    Par défaut
    Citation Envoyé par TheGriffin
    Bonjour,

    L'Invoke permet d'utiliser une méthode Delegate (process), dont le but est de partager l'accès aux objets créés par le Thread principal dans le Thread secondaire (ce qui dans le cas on l'on utilise pas de Delegate lève une exception).
    Invoke permet de faire exécuter par le thread principal une méthode. C'est nécessaire uniquement si la méthode manipule un contrôle depuis un thread qui ne possède pas ce contrôle. Sans ça effectivement tu as droit à une exception car c'est dangereux, risque de deadlock.

    Maintenant regarde ce qui se passe avec ton code : depuis ton thread principal tu crées un thread qui demande au thread principal d'exécuter la méthode storeData. Ya comme un truc qui cloche.


    Mon objet EWSD, ainsi que le Dataset étant créé par le Thread principale, c'est d'après ce que j'ai pu lire la façon la plus propre de partager les objets (Framework 2.0 ou sup).
    C'est ce qu'il faut faire depuis un thread qui accède à des contrôles qui ne lui appartiennent pas. Et encore dans ce cas il faut bien séparer les traitements des accés aux contrôles (dans des fonctions séparées) pour demander au thread principal de ne traiter QUE les accés aux contrôles, sans ça comme dans ton code tu te retrouves avec ton thread principal qui fait tout .

    Sinon tu ne réponds pas à ma question sur le while.

  5. #5
    Inactif  
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Janvier 2007
    Messages
    6 604
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 64
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Janvier 2007
    Messages : 6 604
    Par défaut
    Citation Envoyé par Sphax
    Invoke permet de faire exécuter par le thread principal une méthode.
    Non , pas exactement :ça la fait exécuter par le thread qui a instancié le contrôle, il me semble.

  6. #6
    Membre habitué
    Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    10
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2007
    Messages : 10
    Par défaut
    Autant pour moi,

    Je n'avais pas compris que la question portait sur le While et non sur le Invoke. La raison pour laquelle le Invoke ce trouve dans un While c'est que sans celui-ci l'Invoke n'est exécuté qu'une seul foie pour le premier Record.

    N'ayant pas fait de C depuis plus de 10 ans, et ne faisant du C# que depuis peu, ce qui vous semble évidant ne l'est pas forcement pour moi. Ainsi il me semble que la méthode storeData est belle et bien exécutée par le Thread secondaire, et j'en veux pour preuve que si l'on exécute startReading et non reading, l'ensemble du forme n'est pas figé pendant l'exécution, on peu ainsi déplacer la fenêtre (et au passage le progressBar fonctionne, ce qui n’est pas le cas avec la méthode reading).

  7. #7
    Membre émérite
    Profil pro
    Inscrit en
    Août 2003
    Messages
    835
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2003
    Messages : 835
    Par défaut
    N'ayant pas fait de C depuis plus de 10 ans, et ne faisant du C# que depuis peu, ce qui vous semble évidant ne l'est pas forcement pour moi
    Personnellement je tutoie les gens sur les forums j'espere que ça ne te dérange pas (ça ne me dérange pas d'être vouvoyé en tout cas ). A part ça pas de soucis, on est là pour apprendre, j'espere que ma première réponse n'a pas été trop "sèche", ce n'était pas mon intention.

    Concernant la méthode Control.Invoke, tu trouveras ici la doc officielle, qui explique (assez mal il faut le dire) ce que fait cette méthode. Et ici un petit article en anglais qui semble bien expliquer les problèmes de multithreading et d'accés aux controles.


    Citation Envoyé par TheGriffin
    Ainsi il me semble que la méthode storeData est belle et bien exécutée par le Thread secondaire, et j'en veux pour preuve que si l'on exécute startReading et non reading, l'ensemble du forme n'est pas figé pendant l'exécution, on peu ainsi déplacer la fenêtre (et au passage le progressBar fonctionne, ce qui n’est pas le cas avec la méthode reading).
    Je vois une raison plausible pour laquelle la méthode storeData semble être exécutée par le thread secondaire: dans la méthode readDataLoop tu demandes au thread principal de traiter une ligne en faisant un invoke puis tu le laisses souffler le temps de repasser dans la boucle, ce qui lui laisse le temps de traiter ses messages windows et de mettre à jour la progressbar, tandis que la méthode reading ne le laisse jamais souffler (pour comprendre il faut bien voir que la méthode storeData est exécutée dans le thread principal mais que la boucle est exécutée par le thread secondaire).

    Pour te convaincre et voir quel thread exécute quoi je te conseille de jeter un oeil à l'identifiant du thread courant à 3 moment différents:
    1) au début de l'exécution de ton programme, tu récupèreras l'id du thread principal
    2) au début de la méthode readDataLoop, tu récupèreras l'id du thread secondaire
    3) dans la méthode storeData, où d'aprés moi tu vas retrouver l'id du thread principal

    Cet identifiant tu l'as dans System.Threading.CurrentThread.Id.

    Essaye aussi de compter le nombre de fois que tu passes dans le while de la méthode readDataLoop par rapport au nombre de fois que tu passes dans le while de la méthode reading, ton problème de perf pourrait peut être venir de là (cela dit les appels à la méthode Invoke peuvent aussi induire une perte de perf)

  8. #8
    Membre habitué
    Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    10
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2007
    Messages : 10
    Par défaut
    Ok,
    Je commence à comprendre un peu mieux, effectivement la méthode store data est belle et bien exécutée dans le Thread principal. D’où une inutilité total de mon Thread secondaire vue qu’il ne fais pas grand-chose. Du coup existe-t-il une autre solution que le Invoke pour partager des ressources e entre différents Thread ?

    Et merci pour ton aide.

  9. #9
    Membre émérite
    Profil pro
    Inscrit en
    Août 2003
    Messages
    835
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2003
    Messages : 835
    Par défaut
    Citation Envoyé par TheGriffin
    Ok,
    Je commence à comprendre un peu mieux, effectivement la méthode store data est belle et bien exécutée dans le Thread principal. D’où une inutilité total de mon Thread secondaire vue qu’il ne fais pas grand-chose. Du coup existe-t-il une autre solution que le Invoke pour partager des ressources e entre différents Thread ?
    Il faut distinguer deux choses:
    - les variables partagées par plusieurs threads dont les accés doivent être synchronisés (c'est à dire qu'il faut empêcher que 2 threads touchent en même temps aux mêmes variables + d'autres petites subtilités)
    - les accés à des contrôles par un thread qui n'en est pas propriétaire

    Pour le premier point c'est à toi de voir quelles variables sont partagées par tes threads et gérer la synchronisation si nécessaire, j'ai l'impression qu'il n'y a que la variable storeDs à synchroniser (peut être ewsd aussi).

    Pour le second point, il faut utiliser invoke mais uniquement pour gérer l'accés aux contrôles, pas pour tout ton traitement. En gros je vois bien comme code:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    private void readDataLoop()
    {
          RecordReader rec = ewsd.readNextRecord();
          while (rec != null) //le thread va s'arreter seul qd il aura fini de lire ewsd
          {
              //Tout ton traitement sur la variable rec ici, effectué par le thread secondaire
     
              rec = ewsd.readNextRecord(); //on lit l'enregistrement suivant (toujours dans le thread secondaire)
              this.Invoke(MethodeQuiUpdateLaProgressbar); //update de la progressbar fait par le thread principal
          }
    }
    Citation Envoyé par TheGriffin
    Et merci pour ton aide.
    De rien

    PS: un excellent article sur le multithreading en .net ici.

  10. #10
    Membre habitué
    Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    10
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2007
    Messages : 10
    Par défaut
    Partager le traitement entre les 2 Tread est effectivement ce que j’ai commencer à faire (pour partager la valeur de EWSD, je la passe en paramètre de l’Invoke). Mais malheureusement les performances ne sont guère meilleures.

    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
     
            # region Slow reader (using Thread)
            private void startReading(string fileName)
            {
                storeDs.Tables["Records"].Clear();
                dataGridView.DataSource = null;
                ewsd = new EwsdReader();
                ewsd.open(fileName); 
     
                startTime = System.DateTime.Now;
                readingTread = new Thread(new ThreadStart(readDataLoop));
                readingTread.Name = "ReadingFile";
                readingTread.IsBackground = true;
                readingTread.Priority = ThreadPriority.BelowNormal;
                readingTread.Start();
                startProgression();
            }
            private void readDataLoop()
            {
                RecordReader rec;
                while ((rec=ewsd.readNextRecord())!=null)
                {
                    rec.read();
                    this.Invoke(new recordWriter(storeData),rec);
                }
                this.Invoke(new process(stopReading));
                readingTread.Abort();
            }
     
            private void storeData(RecordReader rec)
            {
                // crearte new row in dataset
                DataRow newRow = storeDs.Tables["Records"].NewRow();
                newRow["Index"] = storeDs.Tables["Records"].Rows.Count + 1;
                newRow["Address"] = rec.address + 1;
                newRow["Type"] = rec.Name;
     
                // read inner data
                PackageReader pkg;
                if ((pkg = rec.packages.identify(100)) != null) newRow["DateTime"] = pkg.fields[1].Value;
                if ((pkg = rec.packages.identify(110)) != null) newRow["ConnectId"] = pkg.fields[2].Value;
     
                // sotre data in dataset
                storeDs.Tables["Records"].Rows.Add(newRow);
            }
            private void stopReading()
            {
                DataView dv = new DataView(storeDs.Records);
                dataGridView.DataSource = dv;
                dataGridView.Refresh();
                stopProgression();
            }
            #endregion
    Hors je n'est pas l'impression que ce soit l'Invoke par lui même qui soit couteux, par exemple l'appelle de l'invoke du progressBar ne fait pas chuter les performances de façon drastique.

    A demain pour la suite...

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

Discussions similaires

  1. Thread et performances
    Par seeme dans le forum Android
    Réponses: 0
    Dernier message: 03/05/2010, 02h24
  2. thread et performance
    Par gene69 dans le forum Débuter avec Java
    Réponses: 6
    Dernier message: 13/06/2008, 18h42
  3. [Linux] Threads, performance
    Par chris_boulot dans le forum Ruby
    Réponses: 8
    Dernier message: 05/04/2008, 18h01
  4. Réponses: 5
    Dernier message: 09/10/2006, 16h20

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