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

Windows Forms Discussion :

Multithread, bonne façon de faire


Sujet :

Windows Forms

  1. #1
    Membre régulier
    Homme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2006
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2006
    Messages : 126
    Points : 93
    Points
    93
    Par défaut Multithread, bonne façon de faire
    Bonjour,

    Je débute en programmation mutithread dans l'environnement .Net.

    J'écris une application qui doit traiter en parallèle des queues de messages différents.
    Afin de suivre la progression de chaque queue je crée autant de TabPage dans l'interface que je n'ai de thread.
    Mes threads envoient régulièrement des Event qui sont récupérés par les TabPages.
    Ça fonctionne assez bien mais j'ai tout de mêmes de temps à autres des incohérences, voir des deadlocks qui se produisent.
    Jusqu'à présent j'ai pu les résoudre mais plus par intuition que par réelle connaissance du problème.

    J'utilise log4net pour créer des logs avec un RollingFileAppender et un ConsoleAppender.
    De plus j'ai redirigé ma console vers différentes TextBox qui filtrent le log et ne l'affiche que dans la Textbox concernant le Thread qui écris du log
    En simple :
    2 thread : "Process 1" et "Process 2"
    2 TextBox = "tb_proc1" et "tb_proc2"
    quand l'écris
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    myLogger.Debug("Process 1 : hello wolrd");
    myLogger.Debug("Process 2 : hello wolrd");
    tb_proc1 affiche : Process 1 : hello wolrd
    tb_proc2 affiche : Process 2 : hello wolrd

    Ce qui se passe évidemment c'est que j'ai donc un accès concurrent au thread d'affichage. Chaque process effectue des Invoke pour appeler le thread d'affichage et le faire modifier.

    C'est là que je ne suis pas certain que ma méthode soit la meilleure.
    Est-ce qu'il faut que je travaille en synchrone ou non ?
    Y-a-t une différence entre ces 2 lignes de code ? :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    object[] args = { param1, param2 };
    // méthode 1
    IAsyncResult ar = myTextBox.BeginInvoke(new MyDelegate(MyMethod), args);
    EndInvoke(ar);
    // méthode 2
    myTextbox.Invoke(new MyDelegate(MyMethod), args);
    Je ne peux pas vraiment produire un code simplifié qui produise les soucis que je rencontre car il faudrait pour ça que je cerne bien le problème ce qui n'est pas le cas ...

    En espérant trouver un peu de lumière :-)

    Ghurdyl

  2. #2
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    avec .invoke, le code attend que l'invoke soit terminé avant de continuer
    avec .begininvoke, l'appel est mis sur la pile et le code continue de s'exécuter sans attendre
    il me semble qu'endinvoke n'est pas obligatoire dans ce cas

    donc .begininvoke c'est si tu ne veux pas perdre de temps et que les args que tu donnes ne vont pas bouger dans le thread, sinon ils bougeront aussi pour le thread principal si c'est des objets

    pour avoir un deadlock il faut par exemple que thread1 attendre un résultat de thread2 et que thread2 attende aussi un résultat de thread1
    si thread1 et thread2 font des invokes sur threadPrincipal en théorie ca ne peut pas causer de deadlocks

    NB : à connaitre pour le multithreading thread-safe : lock et readerwriterlock (readerwriterlockslim sur fx3.5)
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  3. #3
    Membre régulier
    Homme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2006
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2006
    Messages : 126
    Points : 93
    Points
    93
    Par défaut
    Bonjour
    Merci de ta réponse.

    J'ai constaté que mes deadlocks se produisent toujours au même moment, donc il y a une vrai erreur dans ma logique que j'ai du mal à la déterminer.

    Tout ce que je peux dire, c'est que tout mes 3 threads (affichage + 2 autres) sont bloqués dans ce cas.
    Si je clique sur pause dans le debugger de visual studio, je peux voir qu'ils sont en attente soit sur une ligne qui appelle une des méthode de log de log4net (log.debug("some text"); ) soit sur un EndInvoke(...).

    Je ne sais d'ailleurs plus où j'avais vu cette syntaxe d'appel de faire un "begin" et puis un "end" juste après alors que dans ce cas faire un simple "invoke" doit à priori faire la même chose.

    J'ai donc un cas où mon thread d'affichage se met en attente d'un de me autres thread mais je ne comprend pas où.

    Je remarque aussi que si je ne mets que des "begininvoke" sans faire de end, pas de deadlock seulement je perd certain logs.

    Je vais chercher.

    Pour ce qui est de l'instruction "lock" je la connait un petit peu mais les readerwriterlock pas du tout.
    Je vais donc potasser du coté de ces 2 instructions.

  4. #4
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    le readerwriterlock permet plus de performances que le lock dans les cas où il est possible de l'implémenter dans les cas où il y a beaucoup de threads ou beaucoup d'accès aux membres verrouillés
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  5. #5
    Membre régulier
    Homme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2006
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2006
    Messages : 126
    Points : 93
    Points
    93
    Par défaut
    Bonjour,

    Je pense avoir trouvé la source théorique de mes deadlocks.
    (Dans la pratique c'est moins simple)

    Les ressources partagées sont d'une part le thread d'affichage et d'autre part les queues synchronisées via le principe du producteur - consommateur.
    J'ai donc des "lock" lorsque je push/pop mes queues et des "Wait" au niveau des "Invoke".

    Donc la seule possibilité, selon moi, d'avoir un deadlock c'est que dans un lock fait par un de mes thread (!= tread d'exécution) je fasse un invoke de ma thread d'affichage (typiquement la méthode log de log4net qui implicitement invoke le tread d'affichage)

    Dans ce cas, si ma thread d'affichage tentait elle aussi d'obtenir le lock j'ai un deadlock.

    L'idée de rediriger la sortie de log4net dans un textbox doit donc être réalisée minutieusement car (en ce qui me concerne) j'ai l'habitude de logger beaucoup de choses un peu partout, surtout en debug

    Pour le moment je contourne donc le problème en ne faisant que des ".beginInvoke" sans ".endInvoke" (de toute façon en terme de performances de me thread de travail c'est plutôt mieux)

    De ce côté là, j'ai trouvé sur le web une jolie (à mon goût) façon de faire un RichTextBoxAppender qui m'évite de passer par la console que je redirigeais avant.

    J'ai également effectué quelques tests autour de la remarque de pol63
    Citation Envoyé par pol63
    donc .begininvoke c'est si tu ne veux pas perdre de temps et que les args que tu donnes ne vont pas bouger dans le thread, sinon ils bougeront aussi pour le thread principal si c'est des objets
    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
    public partial class Form1 : Form
    {
        public delegate void AppendText(String msg);
        public Form1()
        {
            InitializeComponent();
        }
     
        private void doJob()
        {
            string msg;
            for (int i = 0; i < 10; i++)
            {
                {
                    msg = " message " + i + "\r\n";
                    object[] args = { msg };
                    textBox1.BeginInvoke(new AppendText(textBox1.AppendText), args);
                }
            }
        }
     
        private void button1_Click(object sender, EventArgs e)
        {
            Thread t = new Thread(doJob);
            t.Start();
            Thread.Sleep(1000);
        }
    }
    J'ai constaté que le scope est bien géré. Les messages affichés dans la textbox sont tels que je les attendais.
    À noter que le seul cas d'"erreur" que j'ai trouver c'est si la méthode dojob est écrite comme suit
    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 doJob()
    {
        string msg;
        object[] args = new object[1];
        for (int i = 0; i < 10; i++)
        {
            {
                msg = " message " + i + "\r\n";
                args[0] = msg ;
                textBox1.BeginInvoke(new AppendText(textBox1.AppendText), args);
            }
        }
    }
    Dans ce cas, la TextBox affichera 10x le même message. (et c'est tout à fait logique puisse que la référence de "args" est la même pour tout les appels)
    Je suis toute fois étonné que le premier cas soit passé car à priori, la référence de msg devrait, elle aussi, être commune à tout les appels et je m'attendais à avoir 10x le même message.

    Je flag résolu, même si en définitive j'ai plutôt contourné le problème.

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

Discussions similaires

  1. La "bonne façon" de faire du parallelisme
    Par drunkskater dans le forum Caml
    Réponses: 6
    Dernier message: 30/09/2013, 20h53
  2. Avoir la bonne façon de faire
    Par absot dans le forum Langage SQL
    Réponses: 1
    Dernier message: 13/03/2012, 13h38
  3. [Tableaux] Bonne façon de faire ou non ?
    Par gregetso dans le forum Langage
    Réponses: 10
    Dernier message: 14/10/2008, 14h41
  4. Question sur la bonne façon de faire un JMenuBar
    Par bit_o dans le forum AWT/Swing
    Réponses: 2
    Dernier message: 26/06/2007, 10h26
  5. [Checkbox] La bonne façon et comment faire
    Par Meewix dans le forum Langage
    Réponses: 9
    Dernier message: 19/10/2006, 09h23

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