Calcul dans un thread et affichage progressbar : des commentaires ?

Signaler
Messages postés
497
Date d'inscription
mercredi 7 juillet 2004
Statut
Membre
Dernière intervention
13 juillet 2015
-
Messages postés
497
Date d'inscription
mercredi 7 juillet 2004
Statut
Membre
Dernière intervention
13 juillet 2015
-
Bonjours à tous,





c'est sans doute une chose que tout le monde à déjà rencontré. Un
calcul qui prend du temps, et une form qui reste blanche lorsque l'on
lance ce calcul. J'ai donc parcouru le forum à la recherche d'une
solution, et j'ai vu qu'il fallait mettre le calcul dans un thread
séparé.





Alors voilà, je me demande si la méthode que j'utilise est la bonne au
niveau des performances, et au niveau de l'affichage. C'est la première
fois que j'utilise la notion de multithreading, donc je ne sais pas si
tout est correct : voilà comment je procède :


<hr size="2" width="100%">//Clique sur le bouton : créer le fichier txt :


private void bt_C_Creer_txt_Click(object sender, System.EventArgs e)


{


Thread thread_ecriture = new Thread(new ThreadStart(Remplir_Contenu));


thread_ecriture.IsBackground = true;


thread_ecriture.Priority = ThreadPriority.Highest;


if (bt_C_Creer_txt.Text == "Créer le fichier")


{


//region Démarrer l'écriture


thread_ecriture.Start();


//region Démarrer le rafraichissement de la
progressbar


timer_C_Progressbar.Start();

bt_C_Creer_txt.Text = "Annuler";


bt_C_Remplir_Cases.Enabled = true;


}


else


{


thread_ecriture.Abort();


timer_C_Progressbar.Stop();


bt_C_Creer_txt.Text = "Créer le fichier";


}

}


<hr size="2" width="100%">thread_ecriture.Start();
appelle la fonction qui prend au moins une minute, dans laquelle je met à jour une variable publique int_pourcentage_progressbar1qui me donne le pourcentage d'avancement de ma boucle.


timer_C_Progressbar.Start(); démarre un timer qui, toutes les secondes regarde la valeur de int_pourcentage_progressbar1.


<hr size="2" width="100%">//Rafraichissement de la progressbar :

private void timer_C_Progressbar_Tick(object sender, System.EventArgs e)

{

if (progressBar1.Value < 100)

//rafraichir la progressbar :

progressBar1.Value = int_pourcentage_progressbar1;

else

{

//arréter le rafraichissement de la progressbar :

timer_C_Progressbar.Stop();

bt_C_Creer_txt.Text = "Créer le fichier";

}


}



<hr size="2" width="100%">private void Remplir_Contenu()


{

string contenu = "";


for (int i=0; i < Convert.ToInt32(text_C_Nb_Lignes.Text); i++)


{



float pc = (100 * i) /
Convert.ToInt32(text_C_Nb_Lignes.Text);


int_pourcentage_progressbar1 = Convert.ToInt32(pc);




contenu += "MES";


contenu += i;


contenu += ";";


contenu += DateTime_depart.Date.ToShortDateString();


contenu += ";";



contenu +=
DateTime_depart.TimeOfDay.ToString().Remove(DateTime_depart.TimeOfDay.ToString().Length
- 8,8);


contenu += ";";


contenu += "111.111";


contenu += ";";


contenu += "222.222";


contenu += ";";


contenu += "1";


contenu += ";";


contenu += "60";


contenu += ";";


contenu += "00";


contenu += ";";


contenu += "3500";


contenu += ";";


contenu += "2500";


contenu += "\n";


}


//Remplissage du pourcentage pour la progressbar :


int_pourcentage_progressbar1 = 100;


//region Ecriture dans fichier txt :


try


{



using (StreamWriter sw = new
StreamWriter(Path_Repertoire + text_C_Num_Fichier.Text + ".txt"))


{


sw.WriteLine(contenu);


}


}


catch (Exception a)


{


Console.WriteLine("The file could not be written:");


Console.WriteLine(a.Message);


}


}



<hr size="2" width="100%">Le calcul se déroule bien, et le rafraichissement de la progressbar semble correct. Cependant, je me suis rendu compte que thread_ecriture.Abort(); n'arrete pas le calcul.





Je me demande aussi si cela n'irait pas plus vite d'écrire le string
contenu dans le fichier txt a chaque boucle plutôt qu'une fois à la
fin, car je me suis rendu compte que la durée d'une boucle augmente de
plus en plus que "contenu " est grand. Par exemple, une boucle dure 2ms
au départ et plus de 10ms lorsque "contenu" contient 2000 lignes. A
votre avis ?




1°) Est-ce que vous trouvez cette procédure pour la progressbar correcte ?


2°) Comment annuler le calcul en plein milieu ?





Merci d'avance pour vos commentaires.


Sylvain
A voir également:

5 réponses

Messages postés
168
Date d'inscription
jeudi 28 juin 2001
Statut
Membre
Dernière intervention
18 octobre 2008
7
On peut faire plusierus remarques quant à ton code.

Premièrement, tu définis la prioriété du thread à "Highest". Lorsqu'un thread doit s'exécuter en arrière plan, on place généralement cette valeur à "Low". Quand il s'agit d'un thread de premier plan, sa priorité est mise à "Normal".
Les valeurs "Above normal", "Hight" et "Highest" sont généralement réservés à des tâches du système d'exploitation.

Ensuite, tu fais une légère erreur en rafraichissant ta barre de progression avec un Timer. Il est plus judicieux, et moins risqué, de le faire directement dans le thread de cette façon :

float pc = (100 * i) / Convert.ToInt32(text_C_Nb_Lignes.Text);
if (int_pourcentage_progressbar1 != Convert.ToInt32(pc))
{ progressBar1.Value int_pourcentage_progressbar1 Convert.ToInt32(pc);
}

Enfin, tu fais une erreur dans l'agencement de ton code et c'est pour cela que tu ne parviens pas à arrêter le thread en cours de calcul. Ton code devrait ressembler à cela

private Thread thread_ecriture = null; // A placer à l'extérieur de toute fonction.

private void bt_C_Creer_txt_Click(object sender, System.EventArgs e)
{
// il NE faut PAS écraser l'instance de "thread_ecriture" à l'entrée de
// cette fonction.
// sinon, thread_ecriture.Abort() va arrêter un nouveau thread et pas celui
// qui existait déjà.
if (bt_C_Creer_txt.Text == "Créer le fichier")
{

thread_ecriture = new Thread(new ThreadStart(Remplir_Contenu));
thread_ecriture.IsBackground = true;
thread_ecriture.Priority = ThreadPriority.Highest;

//region Démarrer l'écriture
thread_ecriture.Start();
//region Démarrer le rafraichissement de la progressbar
timer_C_Progressbar.Start();
bt_C_Creer_txt.Text = "Annuler";
bt_C_Remplir_Cases.Enabled = true;
}
else
{
thread_ecriture.Abort();
timer_C_Progressbar.Stop();
bt_C_Creer_txt.Text = "Créer le fichier";
}
}


Pour terminer, il est normal que ton programme ralentisse au fur et à mesure de l'exécution du thread.
Le fait de concaténer des chaînes revient à trainer un sac de plus en plus lourd.
Pour accroître (considérablement) les performances, remplace ta chaîne par un objet StringBuilder :
System.Text.StringBuilder contenu = new System.Text.StringBuilder();

Remplace ensuite les lignes du type
contenu += "MES"
par
contenu.Append("MES")

A la fin, tu feras juste ceci :
sw.WriteLine(contenu.ToString());

J'espère que ça répond à tes questions.
Messages postés
497
Date d'inscription
mercredi 7 juillet 2004
Statut
Membre
Dernière intervention
13 juillet 2015
6
Merci pour ces précisions constructives.



Désolé de ne pas avoir répondu plus tôt je suis reparti sur un autre problème.



Sinon, je viens de modifier la façon dont je rafraichis ma progressbar,
c'est ok. En ce qui concerne les temps d'éxécution, j'ai modifié juste
après mon premier post la façon dont je procédais, et en fait, j'écris
ligne à ligne dans mon fichier, au lieu d'attendre d'avoir un string
énorme. (sorti du contexte, on se poserait des questions lol)



Enfin, en ce qui concerne le problème avec mon thread, j'ai compris
l'erreur. Quand je cliquais sur le bouton, il me faisait une nouvelle
instance de thread_ecriture, donc il ne retourvait plus l'ancien.

Par contre, j'ai juste un petit souci au niveau suivant : quand j'abord
mon thread, il ne ferme pas bien mon objet StreamWriter, et si je veux
recliquer sur le bouton pour recommencer le thread mais dans un nouveau
thread va-t-on dire, et bien il me dit (tout naturellement, je le
concois) :



Une exception non gérée du type 'System.IO.IOException' s'est produite dans mscorlib.dll



Informations supplémentaires : Le processus ne peut pas accéder au
fichier "D:\Essais Fichiers\27.txt", car il est en cours d'utilisation
par un autre processus.



Comment je peux faire pour close mon StreamWriter après un thread.abord ?
Messages postés
168
Date d'inscription
jeudi 28 juin 2001
Statut
Membre
Dernière intervention
18 octobre 2008
7
Regarde dans la documentation du Framework.Net :

dans la description de la méthode object.Abord() (où object est une instance de la classe Thread), on apprend que l'exception

[frlrfsystemthreadingthreadabortexceptionclasstopic.htm ThreadAbortException]

est générée dans le thread après l'invocation à Abord.

En clair, tu peux fermer le flux de sortie comme cela :



private void Remplir_Contenu()

{

try

{

// Code principal du thread ...

}

catch (ThreadAbortException)

{

// Mettre ici le code pour terminer les opération du thread

}



// le code ici NE sera JAMAIS exécuté si le thread a été abandonné

}



Il y a juste un truc à savoir : l'exception se re-produit à nouveau
après le bloc catch. De ce fait, si le thread a été abandonné, le code
situé après catch n'est jamais exécuté (c'est ainsi fait pour que le
thread s'achève à un moment où à un autre).

Il existe une manière d'empêcher l'exception de se reproduire continuellement. Pour cela il faut écrire ceci :



try


{


// Code principal du thread ...


}


catch (ThreadAbortException)


{


// Mettre ici le code pour terminer les opération du thread

Thread.ResetAbort();


}



mais il s'agit là de faire attention à ne pas bloquer l'arrêt du thread :



while (true)

{

try



{



}



catch (ThreadAbortException)



{


Thread.ResetAbort();



}

}



... ça pourrait être gênant... :-)
Messages postés
497
Date d'inscription
mercredi 7 juillet 2004
Statut
Membre
Dernière intervention
13 juillet 2015
6
ok j'ai pigé le coup du abord.



Les 2 lignes suivantes

timer_C_Progressbar.Stop();
bt_C_Creer_txt.Text = "Créer le fichier";

ne sont plus dans private void bt_C_Creer_txt_Click(object sender, System.EventArgs e)

mais on les place directement dans le code de ce que fait le thread, dans la branche qui capte l'exception générée lord du .abord()



Le coup du Thread.ResetAbort(); j'ai pigé aussi, en revanche le coup du while(true), je pense que c'est pour ne pas être bloqué dans le try catch, mais suis pas sur ;)
Messages postés
497
Date d'inscription
mercredi 7 juillet 2004
Statut
Membre
Dernière intervention
13 juillet 2015
6
Sinon, j'ai un chtit problème lorsque j'annule mon thread, donc la
création de mon fichier, il faut supprimer le fichier qui vient d'être
commencé et annulé.



je fais ceci :


while(true)

            {

                try

                {

//Code du thread

}

catch (System.Threading.ThreadAbortException)

                {

                    Thread.ResetAbort();

                    sw.Flush();

                    sw.Close();

           
        File.Delete(Path_Repertoire +
text_C_Num_Fichier.Text + ".txt");

           
        bt_C_Creer_txt.Text = "Cr&#233;er le
fichier";

                    progressBar1.Value = 0;

                }

            }




Mais, une nouvelle exception est gérée :

Une exception non gérée du type 'System.Threading.ThreadStopException' s'est produite dans mscorlib.dll




Informations supplémentaires : Le thread était en cours d'arrêt.


au niveau de la ligne File.Delete(Path_Repertoire + text_C_Num_Fichier.Text + ".txt");



J'ai mis alors Thread.ResetAbort() à la dernière ligne et ca pose plus de problème. Je sais pas si c'est la meilleure méthode



Par contre, ton while(true), cela ne va pas, lorsqu'il y a un arrêt du thread par annulation, le programme retourne dans le code du thread et réexécute le thread !