Thread et Timer

billcapas - 7 août 2015 à 13:02
nagaD.scar Messages postés 4272 Date d'inscription samedi 8 septembre 2007 Statut Membre Dernière intervention 4 janvier 2023 - 7 août 2015 à 14:43
Bonjour,

j'ai un service windows qui lance 4 Thread.... Ok

Dans mes threads j ai instancié l'obj System.Timer... qui me permet de relancer une methode tt les x tps .... ok

Mais alors quand je veux controler mes Threads , c a d savoir leur état de fonctionnement pour les couper ou les relancer la j ai un soucis car le ThreaState indique Stopped et ISALIVE me retourne False alors que le thread tourne car il ecrit dans un fichier de log donc il est en fonction .? c est sur.

j ai supprimer le timer et la plus de probleme j ai acces a mes threads peux interagir avec et ISALIVE me retourne bien true et ThreadState me retour joins sleep ...

je me dis que le thread est surement du au timer dans un etat .??? .??? mais je n ai plus la mains dessus si j ai le system.timer

qqun aurait il une idée ou deja rencontré se probleme

merci a vous

3 réponses

nagaD.scar Messages postés 4272 Date d'inscription samedi 8 septembre 2007 Statut Membre Dernière intervention 4 janvier 2023 17
7 août 2015 à 13:38
Salut ,

Un extrait de ton code serai le bienvenue (lancement des thread, check des thread, etc. les traitements ensuite ne son, a prioris, pas necessaire).

naga
0
billcapas Messages postés 2 Date d'inscription vendredi 7 août 2015 Statut Membre Dernière intervention 7 août 2015
Modifié par billcapas le 7/08/2015 à 13:52
Mon service

public partial class Service1 : ServiceBase
{
......
// classe de thread
Th_Job.Th_RestProgInput _RstIn;
Th_Job.Th_RestProgOutput _RstOut;

Th_Job.Th_GirthInput _G_input;
Th_Job.Th_GirthOutput _G_output;

// instantiation des thread
Thread _Th_RstIn;
Thread _Th_RstOut;

Thread _TH_G_input;
Thread _TH_G_output;
.........


public void INIt()
{

........................................


this._RstIn = new Th_Job.Th_RestProgInput(this._opcServer, this._AS400_Emerson, _XxX);
this._RstOut = new Th_Job.Th_RestProgOutput(this._opcServer, this._AS400_Emerson, _XxX);

this._G_input = new Th_Job.Th_GirthInput(this._opcServer, this._AS400_Emerson, _XxX);
this._G_output = new Th_Job.Th_GirthOutput(this._opcServer, this._AS400_Emerson, _XxX);

this._Th_RstIn = new Thread(new ThreadStart(_RstIn.Run));
this._Th_RstIn.Name = "RstIN";


this._Th_RstOut = new Thread(new ThreadStart(_RstOut.Run));
this._Th_RstOut.Name = "RstOut";

this._TH_G_input = new Thread(new ThreadStart(_G_input.Initialisation));
this._TH_G_input.Name = "_TH_G_input";


this._TH_G_output = new Thread(new ThreadStart(_G_output.InitAppi));
this._TH_G_output.Name = "_TH_G_output";


this.Tab_Thread[0] = _Th_RstIn;
this.Tab_Thread[1] = _Th_RstOut;
this.Tab_Thread[2] = _TH_G_input;
this.Tab_Thread[3] = _TH_G_output;

Tools.ToolsBox.EcrireLog("GW_SERVICE", "INIT : Start TH X");
this._Th_RstIn.Start();

this._Th_RstOut.Start();

this._TH_G_input.Start();

this._TH_G_output.Start();
......................................................................
}




public void ......
{

la je test l etat et si actif je les ferme ......
Tools.ToolsBox.EcrireLog("GW_SERVICE", "Test si thread actif ...");
if (this.Tab_Thread[0] != null)
{
for (int i = 0; i < 4; i++)
{
Tools.ToolsBox.EcrireLog("GW_SERVICE", "INIT: " + this.Tab_Thread[i].Name + " ::Thread [" + i + "] is :" + this.Tab_Thread[i].ThreadState + " :IsLive: " + this.Tab_Thread[i].IsAlive);
if (this.Tab_Thread[i].IsAlive)
{
Tools.ToolsBox.EcrireLog("GW_SERVICE", "INIT : thread: " + i + "est actif on le ferme");
this.Tab_Thread[i].Join();
this.Tab_Thread[i].Abort();
}
else
{
Tools.ToolsBox.EcrireLog("GW_SERVICE", "INIT : Thread null rien a fermer");
}
}
}
}
}



un des thread



voila les morceau concernant les thread dans mon service
0
nagaD.scar Messages postés 4272 Date d'inscription samedi 8 septembre 2007 Statut Membre Dernière intervention 4 janvier 2023 17
7 août 2015 à 14:03
Bon le petit point vis a vis du tableau de thread est bizarre a mes yeux mais ca n'importe pas ^^ (en fait c est surtout le stockage dans un tableau alors qu'ils sont deja disponible directement, mais osef)


Par contre la tu m as fournit le code sans timer (et donc qui fonctionne). Pourrais-tu me coller l'initialisation des (un seul suffirai) timer ?

merci.

naga
0
billcapas Messages postés 2 Date d'inscription vendredi 7 août 2015 Statut Membre Dernière intervention 7 août 2015
7 août 2015 à 14:12
oui le tab n est pas important claire .... alors ici c est le service qui crée les thread et les lance ....

donc tu veux ule code d 'un des threads avec les timer ok
alors le principe des timer c est de relancer la methode tt les x sec . ce principe n est pas de moi j' ai du reprndre un vieux code en VB et refaire a l identique (pour l instant ) mais en c#
J ai ici supprimer les timer et boucler avec un thread sleep , le action est la meme , mais j aimerai comprendre pq je perds le control de mes threads si j implémente le timer .

et merci de m aider c est chouette si besoin je peux aussi t envoie tt mon projet c#VS2010.

namespace Th_Job
{
public class Th_GirthInput
{
// creation d un obj pour lock
public object _Obj;
// timer de relance
// public Timer _Timer_Input_WriteDQ;
// public Timer _Timer_Input_ReadTrigger;
// public Timer _Timer_Input_ReadDQ;
// public Timer _TimerAckDataPlc;

public Elements.OPCServer _OpcServer;
public Elements.Com_AS400 _AS400;

public BusinessLayer.BLTraitementGirth _BLT;

public int NbrEssais;
public int NbrEssaisRdq;


public Data_AS400._dqsi1gw1 Dq1_IN;
private string SDQI_1;

public Data_AS400._dqso1gw1 Dq1_OUT;
private string SDQO_1;

bool _Trigger;

private SaveObj _SaveObj;
// private infosCom _InfosCom;

private MessageQueue _MsgQ;

//
public Th_GirthInput(Elements.OPCServer _OPC, Elements.Com_AS400 _as400,object x)
{
this._Obj = new object();
this._Obj = x;
Tools.ToolsBox.EcrireLog("ThGInput", "Instance du thread G_InPut");

/* _Timer_Input_ReadDQ = new Timer();
_Timer_Input_ReadDQ.Interval = 1000;
_Timer_Input_ReadDQ.Enabled = false;
_Timer_Input_ReadDQ.Elapsed += new ElapsedEventHandler(_Timer_Input_ReadDQ_Tick);
  • /

/* _Timer_Input_ReadTrigger = new Timer();
_Timer_Input_ReadTrigger.Interval = 1000;
_Timer_Input_ReadTrigger.Enabled = false;
_Timer_Input_ReadTrigger.Elapsed += new ElapsedEventHandler(_Timer_Input_ReadTrigger_Tick);
  • /

/* _Timer_Input_WriteDQ = new Timer();
_Timer_Input_WriteDQ.Interval = 1000;
_Timer_Input_WriteDQ.Enabled = false;
_Timer_Input_WriteDQ.Elapsed += new ElapsedEventHandler(_Timer_Input_WriteDq_Tick);
  • /

/* _TimerAckDataPlc = new Timer();
_TimerAckDataPlc.Interval = 1000;
_TimerAckDataPlc.Enabled = false;
_TimerAckDataPlc.Elapsed += new ElapsedEventHandler(_TimerAckDataPlc_Tick);
  • /

// affectation
this._OpcServer = _OPC;
this._AS400 = _as400;

this._BLT = new BusinessLayer.BLTraitementGirth(this._OpcServer);
this._SaveObj = new SaveObj(this._Obj);
// this._InfosCom = new infosCom();

Tools.ToolsBox.EcrireLog("ThGInput", "Instance realisée");
}
/* public void SetinfoCom(string Val)
{
lock (this._Obj)
{
this._InfosCom = (infosCom)this._SaveObj._ReadeObj("c:\\", "Data_Com.dat");
this._InfosCom._Rst_OutPut = Val;
this._SaveObj._WitreObj("c:\\", this._InfosCom, "Data_Com.dat");
}
}*/
public void Initialisation()
{
Tools.ToolsBox.EcrireLog("ThGInput", "Initialisation ");
// ecriture Ack
// _ff._Write_Trigger_424_01_in_Reset(false);

this._MsgQ = new MessageQueue(@"ATI-ST2\Private$\FightWelder1");
this._MsgQ.Send("Initialisation .... ", "_Input_G7");

try
{
NbrEssais = 0;
NbrEssaisRdq = 0;

// raz trigger
this._BLT._Ecriture_421_Trigger(this._OpcServer, false);

//_ff._Write_Trigger_421(false);

Tools.ToolsBox.EcrireLog("ThGInput", "lancement de StepInput_Read_Trigger");

StepInput_ReadTrigger();
}
catch (Exception Ex)
{
Tools.ToolsBox.EcrireLog("TH_GirthInput", " Erreur..." + Ex.ToString() );
}
}


//

public void StepInput_ReadTrigger()
{
while (true)
{
Tools.ToolsBox.EcrireLog("ThGInput", " -> StepInput_REad_Trigger");

NbrEssais = 0;
NbrEssaisRdq = 0;
// coupure timer de relance
//_Timer_Input_ReadTrigger.Enabled = false;
// mise a jour Step plc
// mise a jour du step DB424 int 2 val 10
this._BLT._Ecriture_424_Step_in(this._OpcServer, (Int16)10);

// raffraichir les data PLC DB420

// affichage

// _ff.label_Input.Text = "Attente PLC trigger entrée";
this._MsgQ.Send("Attente PLC Trigger entrée ....", "_Input_G7");

Tools.ToolsBox.EcrireLog("ThGInput", "Attente PLC Trigger entrée");

// lire le trigger si false pas de data si true code barre present
if (this._BLT._Lecture_420_Trig(this._OpcServer, out _Trigger) == 1)
{
// lecture ok
// controle trigger 420.00
if (_Trigger)
{

// trigger a TRUE
// lecture du numéro de serie
string Tps = "";
this._BLT._Lecture_420_SN(this._OpcServer, out Tps);

// affichage
// _ff.textBox_Input_SN.Text = Tps;
this._MsgQ.Send("" + Tps, "InNS");
Tools.ToolsBox.EcrireLog("ThGInput", "lecture Sn 420 ok .... lancement de Step_input_Write_DQ ");

StepInput_WriteDQ();
}
else
{
// _Timer_Input_ReadTrigger.Enabled = true;
Tools.ToolsBox.EcrireLog("ThGInput", "Relance Timer ReadTrigger");
// _EcrireLogFileActif("Girth_Inpuit", "Relance Timer ReadTrigger", _ff.LogFileActif);
}
}
else
{
// erreur de lecture plc
Tools.ToolsBox.EcrireLog("ThGInput", "Erreur de lecture PLC");
// _Timer_Input_ReadTrigger.Enabled = true;
// _EcrireLogFileActif("Girth_Inpuit", "Erreur de lecture PLC", _ff.LogFileActif);
}
Thread.Sleep(1000);
}// end while ....
}

private void StepInput_WriteDQ()
{
while (true)
{
// _EcrireLogFileActif("Girth_Inpuit", "Step_Input_WriteDQ ", _ff.LogFileActif);
Tools.ToolsBox.EcrireLog("ThGInput", "Step_Input_WriteDQ");
// coupure timer
//_Timer_Input_WriteDQ.Enabled = false;
// creation et envoie DQ --> as400.
// mise a jour du step
// _ff._Write_Step_424_In((Int16)20);
this._BLT._Ecriture_424_Step_in(this._OpcServer, (Int16)20);

// _ff.label_Input.Text = "Traitement data pour envoie DQ_In à l'AS400";
this._MsgQ.Send("Traitement data pour envoie DQ_IN -> AS400", "_Input_G7");

Tools.ToolsBox.EcrireLog("ThGInput", "Traitement des data pour envoie DQ");
string Tps = "";
this._BLT._Lecture_420_SN(this._OpcServer, out Tps);

// peparation de la data Q
Dq1_IN._Id_Station = "GW1 ";
DateTime _Da = new DateTime();
_Da = DateTime.Now;
Dq1_IN._date = _Da.ToString("yyyyMMdd");
Dq1_IN._Heure = _Da.ToString("hhmmss");
Dq1_IN._Serial = Tps;

try
{
// concatenation de la chaine a envoyer
SDQI_1 = Dq1_IN._Id_Station + Dq1_IN._date + Dq1_IN._Heure + Dq1_IN._Serial;
// _ff.textBox_SendDQIn.Text = SDQI_1;
this._MsgQ.Send("" + SDQI_1, "InSendDq");
// envoie de la data Q
Tools.ToolsBox.EcrireLog("ThGInput", "Tentative d envoie des datas ....");
if (_AS400._Write_DQ("DQSI1GW1", "PSTOCDAT", SDQI_1))
{
// _ff.label_Input.Text = "DQ_IN envoyée !";
this._MsgQ.Send("DQ_ envoyée ... ", "_Input_G7");

// _EcrireLogFileActif("Girth_Inpuit", "DQ_IN Envoyée", _ff.LogFileActif);
Tools.ToolsBox.EcrireLog("ThGInput", "Datas envoyées sur DQ_IN");
// ecriture de la DQ ok
Tools.ToolsBox.EcrireLog("ThGInput", "lancement de Stepinput_Read_DQ");
StepInput_ReadDQ();
// mise a jour du step IN
}
else
{
// _EcrireLogFileActif("Girth_Inpuit", "Pas d envoie relance timer = nbr= " + NbrEssais, _ff.LogFileActif);
Tools.ToolsBox.EcrireLog("ThGInput", "Pas d envoie sur DQ_IN relance num : " + NbrEssais + "/10 via timer");
// _ff.label_Input.Text = "Erruer Write DQ " + NbrEssais;
this._MsgQ.Send("Erreur d'ecriture DQ ", "_Input_G7");

NbrEssais++;
// _ff.textBox_Nbr_SendInDQ.Text = "" + NbrEssais;
this._MsgQ.Send("" + NbrEssais, "NbrSendDQIn");
if (NbrEssais >= 10)
{
NbrEssais = 0;
Tools.ToolsBox.EcrireLog("ThGInput", "Nombre d'envoie sur DQ_in depassé relance de l application.");
Initialisation();
}
else
{
Tools.ToolsBox.EcrireLog("ThGInput", "SetpInput_WrtiDQ Relance via timer");
//_Timer_Input_WriteDQ.Enabled = true;
}
}
}
catch (Exception EX)
{
// _EcrireLogFileActif("Erreur", "Erreur Write DQ. " + EX.ToString(), _ff.LogFileActif);
Tools.ToolsBox.EcrireLog("ThGInput", "Erreur sur DQ_IN = " + EX.ToString());
// _Timer_Input_WriteDQ.Enabled = true;
}
Thread.Sleep(1000);
}// end while
}

private void StepInput_ReadDQ()
{
while (true)
{
// _EcrireLogFileActif("Girth_Inpuit", "Step Input_ReadDQ ", _ff.LogFileActif);
Tools.ToolsBox.EcrireLog("ThGInput", "StepInptu_Read_DQ");
// coupure timer
// _Timer_Input_ReadDQ.Enabled = false;

// mise a jour step programme
// mise a jour du step IN
// _ff._Write_Step_424_In((Int16)30);
this._BLT._Ecriture_424_Step_in(this._OpcServer, (Int16)30);

// _ff.label_Input.Text = "Attente Retour AS400";
this._MsgQ.Send("Attente Retour AS400 ...", "_Input_G7");

// lecture de la DQ retour

SDQO_1 = _AS400._Read_DQ_Peek("DQSO1GW1", "PSTOCDAT");
if (!SDQO_1.Equals("ERREUR"))
{
// _EcrireLogFileActif("Girth_Inpuit", "Lecture DQ ok & tratement de data" + SDQO_1, _ff.LogFileActif);
Tools.ToolsBox.EcrireLog("ThGInput", "lecture DDQ ok et traitement de data" + SDQO_1);
// ok il y a de la data lecture .
SDQO_1 = _AS400._Read_DQ("DQSO1GW1", "PSTOCDAT");
// traitement de la data ....
// _ff.textBoxRecie_DQIN.Text = SDQO_1;
this._MsgQ.Send("" + SDQO_1, "INRecievDQ");

Dq1_OUT._Id_Station = SDQO_1.Substring(0, 5);
Dq1_OUT._Serial = SDQO_1.Substring(5, 12);
Dq1_OUT._Satus_Ret = SDQO_1.Substring(17, 3);
Dq1_OUT._Recette_Chg = SDQO_1.Substring(20, 2);
Dq1_OUT._Item_num = SDQO_1.Substring(22, 15);
Dq1_OUT._Denomination = SDQO_1.Substring(37, 30);
Dq1_OUT._Satus_Compres = SDQO_1.Substring(67, 2);

// controle Id Station et SN
// pour affichage
string[] _Val = Dq1_OUT.getStrg();
// envoie des data dans le PLC 421
// _EcrireLogFileActif("Girth_Inpuit", "envoie les datas au PLC" + Dq1_OUT.getStrg() + " : " + Dq1_OUT._Satus_Compres + " : " + Int16.Parse(Dq1_OUT._Satus_Compres), _ff.LogFileActif);
// _EcrireLogFileActif("Girth_Inpuit", "envoie les datas au PLC" + Dq1_OUT._Satus_Ret + " : " + _Val[0], _ff.LogFileActif);
// _EcrireLogFileActif("Girth_Inpuit", "envoie les datas au PLC" + Dq1_OUT._Recette_Chg + " : " + _Val[1], _ff.LogFileActif);
// _EcrireLogFileActif("Girth_Inpuit", "envoie les datas au PLC" + Dq1_OUT._Item_num + " : " + _Val[2], _ff.LogFileActif);
// _EcrireLogFileActif("Girth_Inpuit", "envoie les datas au PLC" + Dq1_OUT._Denomination + " : " + _Val[3], _ff.LogFileActif);
// _EcrireLogFileActif("Girth_Inpuit", "envoie les datas au PLC" + Dq1_OUT._Satus_Compres + " : " + _Val[4], _ff.LogFileActif);
//_ff._Write_DATA_421(Dq1_OUT.getStrg());


// _ff._Write_DATA_421_Satus_Compresseur(Int16.Parse(Dq1_OUT._Satus_Compres));
this._BLT._Ecriture_421_DATA_Status_Compresseur(this._OpcServer, Int16.Parse(Dq1_OUT._Satus_Compres));

// _ff._Write_DATA_421_Satus_Ret(Int16.Parse(Dq1_OUT._Satus_Ret));
this._BLT._Ecriture_421_DATA_Status_Ret(this._OpcServer, Int16.Parse(Dq1_OUT._Satus_Ret));

//_ff._Write_DATA_421_Type_Donne(Int16.Parse(Dq1_OUT._Recette_Chg));
this._BLT._Ecriture_421_DATA_Type_Donne(this._OpcServer, Int16.Parse(Dq1_OUT._Recette_Chg));

// _ff._Write_DATA_421_SN(Dq1_OUT._Serial);
this._BLT._Ecriture_421_DATA_SN(this._OpcServer, Dq1_OUT._Serial);

// _ff._Write_DATA_421_Item_Num(Dq1_OUT._Item_num);
this._BLT._Ecriture_421_DATA_Item_Num(this._OpcServer, Dq1_OUT._Item_num);

//_ff._Write_DATA_421_Denomination(Dq1_OUT._Denomination);
this._BLT._Ecriture_421_DATA_Denomination(this._OpcServer, Dq1_OUT._Denomination);


// envoie du trigger fin de data sur plc 421

// _ff._Write_Trigger_421(true);
this._BLT._Ecriture_421_Trigger(this._OpcServer, true);
Tools.ToolsBox.EcrireLog("ThGInput", "Ecriture PLC ok .... lancement StepInput_AckDataPLC");
// ACKDATAPLC
StepInput_AckDataPlc();
}
else
{
// _EcrireLogFileActif("Girth_Inpuit", "Relance timer sur = " + NbrEssaisRdq, _ff.LogFileActif);
Tools.ToolsBox.EcrireLog("ThGInput", " pas de lecture DQ Relance " + NbrEssaisRdq + "/10 via timer");
//_ff.label_Input.Text = "Pas de retour AS400 = " + NbrEssaisRdq;
this._MsgQ.Send("Pas de retour AS400", "_Input_G7");

NbrEssaisRdq++;
//_ff.textBox_Nbr_RecivInDQ.Text = "" + NbrEssaisRdq;
this._MsgQ.Send("" + NbrEssaisRdq, "NbrRecieveDQIn");
if (NbrEssaisRdq >= 10)
{
Tools.ToolsBox.EcrireLog("ThGInput", "nombre de tentative de lecture DQ dépassée relance de l application ");
NbrEssaisRdq = 0;
Initialisation();
}
else
{

//_Timer_Input_ReadDQ.Enabled = true;
}
}
Thread.Sleep(1000);
}// end while
}


private void StepInput_AckDataPlc()
{
while (true)
{
// _EcrireLogFileActif("Girth_Inpuit", "Step_AckdataPlc", _ff.LogFileActif);
Tools.ToolsBox.EcrireLog("ThGInput", "Step_AckDataPlc");
//coupure timer
//_TimerAckDataPlc.Enabled = false;

//
// mise a jour Step
// _ff._Write_Step_424_In((Int16)40);
this._BLT._Ecriture_424_Step_in(this._OpcServer, (Int16)40);

// _ff.label_Input.Text = "Attente PLC trigger ....";

this._MsgQ.Send("Attente Trigger PLC ....", "_Input_G7");

// lecture DB420

// if (_ff._Read_Input_420_TG(out Trigger))
if (this._BLT._Lecture_420_Trig(this._OpcServer, out _Trigger) == 1)
{
if (!_Trigger)
{
// opc inputWrite 421
// _ff._Write_Trigger_421(false);
this._BLT._Ecriture_421_Trigger(this._OpcServer, false);

// _EcrireLogFileActif("Girth_Inpuit", "AckData ok relance ... l api", _ff.LogFileActif);
Tools.ToolsBox.EcrireLog("ThGInput", "AckDataPLC ok .... fin et relance de application");
this._MsgQ.Send("Ack ok ... Relelance de l application ...", "_Input_G7");
StepInput_ReadTrigger();
}

// fin
}
Thread.Sleep(1000);
}// end while
}
0
nagaD.scar Messages postés 4272 Date d'inscription samedi 8 septembre 2007 Statut Membre Dernière intervention 4 janvier 2023 17
7 août 2015 à 14:43
d'accord alors grossierement ce que j en pense (je ne peux pas prendre le temps de bien tout analyser mais bon): Le soucis en fait que tu rencontre serai le lancement en simultané des même tâches (à intervalle regulier) ... je m explique :

un traitement est lancé tous les X secondes. Or dans certains cas le temps de traitement est supérieur à ces X secondes et donc on obtient au final plusieurs thread alors que l on en souhaite un seul => de ce fait lorsque le premier thread est terminé, le second lui tourne toujours (ce qui pourrait donc être le soucis actuel, en tout cas je penses).

Donc ce qui serai interessant de faire c'est, pour faire au plus simple bien que pas "propre", de creer une classe accessible de partout avec, pour ton cas, 4 booleens (un par thread) => lors du lancement d un thread, la premiere chose qu'il fait c est checker cette valeur :
-si elle est à "vrai" : un thread tourne déja => stop.
-si elle est à "faux" : rien en cours => on continue en changeant ce flag (pour le futur thread)

Et a la fin du traitement on indique "vrai".

Alors c est pas top mais ca sera rapide a faire et tu pourras tester. (ou alors pour vraiment faire au plus simple, tu log les infos thread. Pour la class de test, perso je ferai une classe static (ne pas oublier tout ce qui est invoke etc.).

Bon j aimerai bien que quelqu un d autre donne son avis aussi ^^

demande si je suis pas clair ;)

naga


PS : perso je m'embete pas, ce que je fais c'est relancer le traitement apres X secondes à la fin d un thread plutot que le lancer a interval regulier (mais dans ce cas il faut que le traitement soit unique => pour l ecoute d'un port par exemple ce n est en general pas le cas : on a un thread d ecoute qui genere les thread de traitement lors de la reception d info donc incluant des traitements paralleles ... enfin bref ^^).
0
Rejoignez-nous