Forum Clubic

Problème de thread en C#

Bonjour,

J’ai un gros souci avec 3 threads , je vous explique la situation.

//Thread 1 écriture

while(true)
{
//code

Thread.sleep(3600000) // 1H
}

//Thread 2 et 3 lecture

while(true)
{
//code

Thread.sleep(3600000) // 1heure
}

while(true)
{
//code

Thread.sleep(1000) // 1sec
}

Au démarrage de l’application les 3 threads sont démarré via la méthode Start();

L’application doit resté ouverte 24h/24 7j/j car elle collecte des données sur un réseau.

Avant de mettre l’application en service , j’ai éffectué des tests. J’ai donc laissé la machine allumé une nuit complète. Le lendemain en arrivant j’ai constaté que l’application avait planté. J’ai alors décidé de crée un journal pour collecté des données à chaque écriture/lecture dans la boucle. J’ai relancé l’application une nuit complète.Alors j’ai recommencé le test et ça a encore planté.
Mais cette fois-ci j’ai constaté que ce sont les threads de lecture qui ont planté , l’écriture continuait malgré que le programme soit planté. Je ne comprend pas pourquoi c’est arrivé. pourtant mes threads ne se partage pas des ressources critique, il travaille totalement séparément de leur coté. De plus je sais pas si c’est une coincidence mais l’heure à laquel l’application à planté les 2 jours est exactement la même. ( 16h30 -> 2h30 du matin)

Pouvez vous m’aider à eclaircir ce mystère car je ne comprend vraiment pas et je suis bloqué tant que j’ai pas résolu cette panne.

PS : cela ne provient pas d’un problème réseau ou d’une machine ,j’en suis sur à 100% , c’est un problème d’éxécution dans le code.

Sous Windows XP? Vérifie à tout hasard avant de retester que tu n’as pas le service mise à jour automatique en marche vers ces heures là. Moi quand je faisais des tests de montées en charge, ça avait chargé des conneries, et rebooter…

Oui c’est bien windows xp.

Merci d’avoir répondu, je vais aller voir ça de plus près.

Hum je l’ai posté finalement? mince : faut être logique, si les threads en écriture fonctionnent encore, c’est que c’est pas du à un reboot. La cause est autre.

Tu peux essayer de faire une thread contrôleur qui vérifie la présence de ta thread?

Sinon, en lecture tu libères bien tout? Tu lis quoi, fait quoi? Car ça peut être un fatal OutOfMemoryError .

Je voudrais savoir si ce que tu dis ça peut réelement perturbé l’application alors que les PC ne sont pas branché sur le net. Donc la mise à jour ne pourra jamais se faire.

Ce sont les threads de lecture qui plantent ? Alors commence par le commencement : essaie de faire tourner le code de lecture toute la nuit dans une application conçue pour l’occasion (sans threads), ça constituera un début de piste :slight_smile:

bestdoom> en fait, je pensais que c’était tout le programme qui plantait; mais tu dis que seul la thread en écriture plante, donc l’ordi est toujours présent et démarré.

Maintenant si c’est du réseau, peut-être que l’ordi sur lequel tu te connecte se déco, dans ce cas ta thread en lecture devrait faire attention à rouvrir la connexion une fois, et s’autoriser à le refaire au bout d’une heure.

Et dernière chose, je sais pas comment fonctionne le garbage collector en C#, mais si jamais il advenait que ta thread en lecture utilisait trop de mémoire, tu pourrais avoir comme en Java un "OutOfMemoryError".

Sous XP, tu as de quoi monitorer l’utilisation de la mémoire et autres dans Outils d’adminstration.

Ce sont les threads en lecture qui plantent.

J’ai suivi vos conseil et j’ai fait une appli test en 30 sec qui résume ma situation.

Je l’ai lancer ce matin à 10h55 , et elle à déja fait 6 écriture. La lecture se passe bien également. Je vous tiendrais au courant.

Je précise que la machine est dans un environnement ou elle est exempté de coupure d’électricité et n’est pas branché sur internet.

La machine ne se déco pas réseau , le câble est soudé à la carte réseau. C’est une machine industrielle.

Pour ce qui est de l’outofmemory je pense pas qu’il est un problème à ce niveau, le process en mémoire est tout à fait stable.


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace ThreadTest
{
    public partial class Form1 : Form
    {
        private Thread timeThread;
        private Thread dateThread;
        private Thread lectureHeureThread;
        private Thread lectureDateThread;
        private Thread ecritureHeureDateThread;
        double j = 0;
        int i = 0;

        public Form1()
        {
            InitializeComponent();

            timeThread = new Thread(new ThreadStart(timeLive));
            timeThread.Start();
            dateThread = new Thread(new ThreadStart(dateLive));
            dateThread.Start();  

        }

        public void timeLive()
        {
            while (true)
            {
                this.label1.Text = this.returnTime();
                Thread.Sleep(1000);
            }
        }

        public void dateLive()
        {
            while (true)
            {
                this.label2.Text = this.returnDate();
                Thread.Sleep(3600000);
            }
        }

        String returnDate()
        {
            string dateNow = null;
            DateTime now = DateTime.Now;
            dateNow = now.Day + "/" + now.Month + "/" + now.Year;
            return dateNow;
        }


        String returnTime()
        {
            string timeNow = null;
            int hour;
            int minute;
            int second;
            string hS, mS, sS;
            DateTime now = DateTime.Now;
            hour = now.Hour;
            minute = now.Minute;
            second = now.Second;
            if (hour < 10)
                hS = "0" + hour;
            else
                hS = "" + hour;
            if (minute < 10)
                mS = "0" + minute;
            else
                mS = "" + minute;
            if (second < 10)
                sS = "0" + second;
            else
                sS = "" + second;
            timeNow = hS + ":" + mS + ":" + sS;
            return timeNow;
        }

        private void button1_Click(object sender, EventArgs e)
        {

            lectureHeureThread = new Thread(new ThreadStart(timeCPU));
            lectureHeureThread.Start();
            lectureDateThread = new Thread(new ThreadStart(dateCPU));
            lectureDateThread.Start();
           

        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {

            ecritureHeureDateThread= new Thread(new ThreadStart(ecrireHeureDate));
            ecritureHeureDateThread.Start();
           
        }


        private void timeCPU()
        {
            while (true)
            {
                traitementHeureCpu();
                Thread.Sleep(1000);
            }
        }

        private void dateCPU()
        {
            while (true)
            {
                traitementDateCpu();
                Thread.Sleep(3600000);
            }
        }

        private void ecrireHeureDate()
        {
            while (true)
            {
                traitementEcritureHeureDateCpu();
                Thread.Sleep(3600000);
            }
        }

        private void traitementEcritureHeureDateCpu()
        {

            label3.Text = "Je change j";
            label4.Text = "Je change i";
       
            
        }

        private void traitementHeureCpu()
        {
             label3.Text = "Valeur de J : "+ j +"";
             j++;   
        }

        private void traitementDateCpu()
        {
            label4.Text = "Valeur de I : " + i + "";
            i++;  
        }
    }
}

Une petite recommandation vite fait :

 lectureHeureThread = new Thread(new ThreadStart(timeCPU));
           lectureHeureThread.Start();
           lectureDateThread = new Thread(new ThreadStart(dateCPU));
           lectureDateThread.Start();

Je te conseille de stopper le précédent thread (s’il existe) avant d’en lancer un nouveau, quand tu cliques sur un bouton. Sinon, si l’utilisateur clique plusieurs fois, tu vas te trouver avec des threads non référencés. Le garbage collector devrait s’en charger, mais dans le doute c’est à éviter.

Sinon, c’est du .NET 1.1 ou 2.0 ? En 2.0, il ne faut pas modifier un élément de l’interface (comme un label) avec un autre thread que celui qui a créé la fenêtre (donc en général le thread principal). Il faut à la place utiliser la méthode Form.Invoke pour appeler une fonction qui elle se chargera de modifier l’élément de l’interface. Les contrôles ne sont en effet pas thread safe (donc ça peut poser des problèmes si deux threads les modifient simultanément). D’ailleurs, il vaut mieux appliquer ce principe aussi en 1.1.

Enfin pour l’instant je ne vois rien qui pourrait justifier un plantage…


 lectureHeureThread = new Thread(new ThreadStart(timeCPU));
          lectureHeureThread.Start();
          lectureDateThread = new Thread(new ThreadStart(dateCPU));
          lectureDateThread.Start();

Le bouton est verouillé une fois que les threads sont démarré. Impossible de recliqué sur le bouton.

Je ne comprend pas quand tu dis de stoppé le thread précédent.Je suis obligé de les lancer tout les 2 en même temps.

J’utilise le framework 2.0. Je vais suivre ton conseil avec form.invoke et voir si ça marche.Au mois ça sera plus propre. Merci kookiz33

PS : Pour info il est 19h30 et l’application tourne toujours correctement.

Tu n’aurais pas par hasard un exemple à me proposé avec Form.Invoke ?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace TestInvoke
{
    public partial class Form1 : Form
    {
        public delegate void updateFunction(string text);

        private Thread thread1;

        public Form1()
        {
            InitializeComponent();
        }

        private void updateLabel(string texte)
        {
            label1.Text = texte;
        }

        private void threadFunction()
        {
            Thread.Sleep(1000);
            this.Invoke(new updateFunction(updateLabel),"OK");
        }

        private void button1_Click(object sender, EventArgs e)
        {
            thread1 = new Thread(new ThreadStart(threadFunction));
            thread1.Start();
        }
    }
}

J’espère que l’exemple est suffisament clair :wink:

Voila l’erreur que j’obtiens lorsque je lance l’application en déboggage et lorsque je la ferme :

*ici
Impossible d’appeler Invoke ou BeginInvoke sur un contrôle tant que le handle de fenêtre n’a pas été créé.

Dans returnDate() , j ai la date en string et dans returnTime() j’ai l’heure en string.


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;
using System.Collections;
using System.Diagnostics;
using System.IO;

namespace ClientOpcRuau
{
    public partial class ClientOpc : Form
    {

           public delegate void updateFunction(string text);

          public ClientOpc()
        {
            InitializeComponent();
            timeThread = new Thread(new ThreadStart(timeLive));
            timeThread.Start();                
            dateThread = new Thread(new ThreadStart(dateLive));
            dateThread.Start();

           
        }

public void timeLive()
        {
            while (true)
            {
                Thread.Sleep(1000);
                this.Invoke(new updateFunction(updateLabel1),returnTime());    *ici
            }
        }

        private void updateLabel1(string texte1)
       {
           WindowsTime.Text = texte1;
       }

        public void dateLive()
        {
            while (true)
            {
                Thread.Sleep(1000);
                this.Invoke(new updateFunction(updateLabel2), returnDate());  *ici
            }
        }

        private void updateLabel2(string texte2)
        {
            WindowsDate.Text = texte2;
        }


et en plus lorsque je met le sleep après l’appel à la fonction , il plante carrément au démarrage de l’application.

La fenêtre doit être créée pour pouvoir utiliser invoke. Il faudrait donc que tu lances tes threads après l’initialisation de la fenêtre, et stopper tes threads avant sa destruction (il y a des évenements pour ça) :wink:

Normalement la fenêtre est crée apres le initializecomponent non ? c’est bon ça fonctionne j ai rajouté les évenements à la fermeture de la fenêtre. Maintenant je vais testé lundi l’application au boulot avec cette solution. Je te tiens au courant de la situation. Merci à toi pour ton aide ainsi qu’a Sans Nom. :super:

Salut, j’ai dit que j’allais te tenir au courant de la soluce et ça à l’air de marché. Ca n’a pas encore planté mais j’ai d’autre problème. La lecture et l’écriture fonctionne bien maintenant.

Par la même ocassion , comment tu peux rendre une interface graphique non quitable , c’est à dire qui bloque la croix pour empecher la fermeture de la form.

Merci

En en faisant une fenêtre modale, mais ça implique de créer une fenêtre parente. Me semble que tu peux le faire sans avoir une fenêtre modale, mais je connais pas C# (pour info, ce dont je te parle je crois que je l’avais fais en Delphi.)

Merci , je vais me renseigner sur les fenêtre modales.

Je sais pas si je me suis bien exprimé : tu dois pouvoir faire disparaître le bouton fermer (la croix). Il me semble que j’y étais parvenu en Delphi, et en Java aussi.

Ca n’empêchera jamais que le programme soit tué via le gestionnaire de tâche ceci dit.

Pour désactiver uniquement le boutton http://img443.imageshack.us/img443/6334/fermervw8.gif il suffit d’effacer
uniquement ce qu’il y a d’encadrer

private System.ComponentModel.IContainer components = null;
    protected override void Dispose(bool disposing)
    {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
    }

Si malgré tout tu veu pouvoir fermer la fenêtre :

        private void button1_Click(object sender, EventArgs e)
        {
            Application.ExitThread();
            Application.Exit();
        }

Je sais c’est barbar comme méthode, mais j’avais pas envie d’utiliser des API windows. :MDR

Après on peut même utiliser le boutton fermer pour autre chose.

        protected override void Dispose(bool disposing)
        {
            this.ClientSize = new System.Drawing.Size(400, 400);
            base.Opacity = 0.5;
        }