[C#]Le thread où l'on parle de thread

Bonjour à tous.

Dans le développement de mon soft de cataloguage de CD, je me trouve confronté à un problème :
j’aimerais qu’une des opérations de mon soft passe par threading de manière à rendre l’application plus réactive et pouvoir annuler la tâche en cours (en l’occurence, le cataloguage d’un CD).

La question est : comment faire ?

Globalement, voilà comment se déroule l’action pour l’instant :

  • Je décide par un des moyens à ma disposition de cataloguer le CD (boutons direct ou détection automatique d’insertion).
  • J’affiche une fenêtre modale comprenant le bouton annuler.
  • Je lance la fonction Cataloguage() de la fenêtre parente qui fait elle même appel à des sous fonctions…

Le bouton annuler ne fonctionne pour l’instant pas.

COMMENT puis-je faire un thread en C# encapsulant la fonction de cataloguage et comment pourrais-je annuler cette fonction si elle est déjà appellée et en cours d’exécution (la fonction de cataloguage peut être longue selon le nombre de fichiers présents sur le CD scanné) ?

Toute aide est la bienvenue, merci ! :slight_smile:

C’est pas exactement ça, mais presque (si c’est comme en Java). Deux solutions :

  • Tu fais hériter ta classe faisant le cataloguage à partir de la classe Thread. Par contre, problème si ta classe hérite déjà d’uen autre classe, notamment si tu fais tes traitements dans ta classe de fenêtre, ce qui de toute façon n’est pas bon.
  • Tu crées une sous classe-de la classe Thread et tu lui ajoutes les méthodes de cataloguage (solution que j’utilise quasiment tout le temps, surtout pour la prog socket multi-connexion). Ensuite, tu crées une instance de cette classe, tu lances sa méthode Run() et hop.

[quote]Bref, c’est le bordel, je crois que je vais faire un joli copy/past du code que universal_tonton m’a gentiment donné… ;).[/quote]Sans garantie, hein :mad: J’ai pondu ce bout de code un peu à l’arrache, je ne l’ai pas testé.

urdle:

[quote]ou alors est-ce qu’il disent que les parties statiques de la classe sont gérées de facon à supporter le multi-threading (et donc que chaque thread peut créer/modifier des instances de cette classe à lui sans problème, car les membres statiques que se partagent les différentes instances ne vont pas provoquer de conflit entre les threads); mais que parcontre, il faut pas partager et faire joujou avec la meme instance depuis 2 thread différents ?[/quote]Je pense que c’est plutôt ça, oui. En fait çe me parait même très clair.

[Edité le 16/1/2003 par universal_tonton]

[Edité le 16/1/2003 par universal_tonton]

quelqu’un peut-il m’expliquer ce que signifie ca (à propos d’une classe) :

[quote] Thread Safety
Any public static (Shared in Visual Basic) members of this type are safe for multithreaded operations. Any instance members are not guaranteed to be thread safe.[/quote]
j’ai trouvé ca sur la msdn, ici

j’ai un peu du mal là…

est-ce qu’ils disent que si tu créé une instance statique, c’est bon pour le multithread, mais les instances non-statiques, ils garantissent pas ?

ou alors est-ce qu’il disent que les parties statiques de la classe sont gérées de facon à supporter le multi-threading (et donc que chaque thread peut créer/modifier des instances de cette classe à lui sans problème, car les membres statiques que se partagent les différentes instances ne vont pas provoquer de conflit entre les threads); mais que parcontre, il faut pas partager et faire joujou avec la meme instance depuis 2 thread différents ?

j’parle d’un event, avec un wait sur event… j’connais les fonctions en c/c++ sous windows, pas en c#
ca fait une attente passive

[quote]bah p’tet que le suspend il pue… mais bon, sinon tu fini l’action en cours et quand tu teste ta variable d’annulation, avant, tu fais une attente si la variable « attente_confirme_annule » est à 1…[/quote]Disons que je le teste, mais c’est vrai que tant que je n’ai pas répondu au messagebox, ma variable n’est pas renseignée et (étrangement) les sous-fonctions continuent de boucler…

Par ailleurs, je ne peux pas la renseigner avant de répondre sinon mes sous-fonctions vont faire un return; et je perdrais des infos si j’annule l’annulation !!! :smiley:

Et puis une attente… tu parles d’un timer ou d’un bouclage à vide ? C’est pas top top, ce genre de solutions… :frowning:

On va plutôt la faire à la c0unt0 si le code d’universal_tonton fonctionne…

bah p’tet que le suspend il pue… mais bon, sinon tu fini l’action en cours et quand tu teste ta variable d’annulation, avant, tu fais une attente si la variable “attente_confirme_annule” est à 1…

Hummmm, dans mon histoire de threads j’ai rencontré un petit problème…

On va faire simple :

  • J’ai la fenetre d’annulation
    -> qui appelle la fonction encapsulée dans un thread Cataloguage()
    -> qui appelle elle-même une fonction récursive LectureRepertoire() où je teste la variable booléenne d’annulation
    -> qui appelle elle-même X fois la fonction récursive AjoutFichierBase() où je teste également la variable booléenne d’annulation

Le thread.suspend() au moment de l’affichage du messagebox de confirmation d’annulation ne met pas vraiment le thread en pause : mon appli plante sur la fonction AjoutFichierBase sur la ligne BarreProgression.Value++; (une barre de progression visible dans la fenêtre principale).

Je ne comprenais pas pourquoi il execute cette ligne alors que le thread est mis en pause.
J’en suis arrivé à la conclusion que le thread ne concerne que cataloguage() et n’inclut pas les fonctions appellées par celle-ci. Les fonctions LectureRepertoire() et AjoutFichierBase() deviennent donc indépendantes ?

Bref, c’est le bordel, je crois que je vais faire un joli copy/past du code que universal_tonton m’a gentiment donné… :D.

[edit : universal_tonton, pas Xentyr… Mais merci quand même ! :(]

[Edité le 16/1/2003 par use-writer]

[quote]Par contre, il y a quelque chose qui m’a un peu fait grincer les dents : on boucle à vide . Le mieux est de faire attendre le thread et de le « réveiller » après. Comme ça, on consomme 0 ressource :wink: En Java, on utilise wait() et notify() pour la synchronisation, pour .net je crois qu’il existe une classe ThreadWaitHandle ou un truc du genre.[/quote]Héhé, en fait, j’avais déjà en tête le coup du timer, c’est pour ça que j’ai écrit le 1er truc qui me passait par la tête, hum et SAIMAL.

Donc pour moi, plutôt que de faire attendre le thread (j’aurais pas fait de boucle à vide, non mais ;)), je préfère mettre un timer, j’aime pas que les choses restent suspendues aux actions de mes gentils utilisateurs quand ce n’est pas indispensable :D. Oui, j’ai eu quelques mauvaises histoires avec des Suspend, et du coup, tant que je peux éviter et que ça reste propre (et rapide), je le fais. Ouaip méfiant le xentyr…

Bref, timer powahhh. Si le mec vient d’appuyer sur Annuler, tu lui donnes 10 secondes pour se rétracter :D. Oui je suis un pro-ultimatum :smiley:

Si je ne me gourre pas :

public delegate void CancelEventHandler(object sender, EventArgs e);

Application.Idle+=new CancelEventHandler(IlFautAnnuler);

public void IlFautAnnuler(Object sender, EventArgs e)
{
/** Fais ton bordel ici */
}

Tu sembles mieux connaitre .net que moi urdle, donc je te fais confiance au sujet des Suspend() et consoeurs. Enfin, ces méthodes étaient bien présentées comme sûres dans le JDK 1.0… Je garde ma méthode, on sait jamais :slight_smile:

Voici la raison de l’obsolescence de la méthode stop() en java :

Because it is inherently unsafe. Stopping a thread causes it to unlock all the monitors that it has locked. (The monitors are unlocked as the ThreadDeath exception propagates up the stack.) If any of the objects previously protected by these monitors were in an inconsistent state, other threads may now view these objects in an inconsistent state. Such objects are said to be damaged. When threads operate on damaged objects, arbitrary behavior can result. This behavior may be subtle and difficult to detect, or it may be pronounced. Unlike other unchecked exceptions, ThreadDeath kills threads silently; thus, the user has no warning that his program may be corrupted. The corruption can manifest itself at any time after the actual damage occurs, even hours or days in the future.

Concernant les section critiques, tu peux parfaitement les protéger en utilisant le mot-clé synchronized.

effectivement, suspend() (associé à resume()) ne fait que mettre le processus en pause… il patiente là où il en est dans son execution, gentillement… c’est donc bien l’effet voulu par use-writer. Ces fonctions fonctionnent parfaitement et sont parties integrantes de tout système d’exploitation multi-tache digne de ce nom (ca marche aussi bien sous WindowsNT&co que sous linux).

Seul soucis de Suspend() : quel est son comportement lorsque le thread suspendu est en pleine utilisation d’une dll, en train de faire une copie de fichier… etc… aucune idée.

Si Suspend() et Stop() ont été virés de Java, c’est surement parceque les machines virtuelles étaient pourries, et que ca faisait planter un paquet de client (C# me semble plus complet au niveau des fonctions système, mais c’est peut-etre qu’une impression… je suis pas sur qu’on puisse definir une zone d’execution critique en java, c’est à dire une zone où y’a que le thread en cours qui s’execute; sans quoi faire un suspend() en plein milieu d’une reservation de mutex peut avoir des conséquences plus qu’étranges)

[quote]Pour le thread, c’est actement ce qu’il faut faire, à un petit détail prêt, peut-être. Je ne suis pas un fan des méthodes stop(), suspend(), etc. (je suppose que c’est ce que tu voulais utiliser en cliquant sur Annuler).[/quote]Non non, la fin du thread se fait par la fin de la fonction qui teste la valeur de la variable. Le fait d’utiliser la suspension permet juste de donner davantage la main à l’utilisateur.

[quote]Par contre, il y a quelque chose qui m’a un peu fait grincer les dents : on boucle à vide . Le mieux est de faire attendre le thread et de le “réveiller” après. Comme ça, on consomme 0 ressource En Java, on utilise wait() et notify() pour la synchronisation, pour .net je crois qu’il existe une classe ThreadWaitHandle ou un truc du genre.[/quote]Justement, le fait d’utiliser suspend() et resume() (je crois que ce sont ces mots-clé dont il s’agit) met en pause le thread le temps de savoir si l’utilisateur décide ou non d’annuler la tâche. Ca équivaut à une pause dans le code non ?
Je me trompe ???

Sinon, j’aurais bien utilisé la méthode de c0unt0 (qui me semble plus adaptée) mais indiquez moi l’équivalent du fenetre::idle en C# en ce cas !!! :mad:

[Edité le 15/1/2003 par use-writer]

Effectivement, un 4ème état ne fait pas de mal :slight_smile:

Par contre, il y a quelque chose qui m’a un peu fait grincer les dents : on boucle à vide . Le mieux est de faire attendre le thread et de le « réveiller » après. Comme ça, on consomme 0 ressource :slight_smile: En Java, on utilise wait() et notify() pour la synchronisation, pour .net je crois qu’il existe une classe ThreadWaitHandle ou un truc du genre.

[quote]Je préfère placer une variable qui indique l’état et que l’ont lit à chaque occurrence de la boucle dans le thread. Du style : 0 - on continue, 1 - on a appuyé sur Annulé, 2 - Fin du catalogage. Dans les deux derniers cas, on sort tout simplement de la boucle et on met le thread à null.

En plus comme tu places déjà une variable pour indiquer si le catalogage a été annulé ou non, autant t’en servir à fond :)[/quote]Je suis aussi de ton avis, sauf que j’aurais justement rajouté un état : celui où il demande confirmation. Car sinon, le prog continue son catalogage tant qu’on n’a pas confirmé l’annulation ! Donc 3 - on boucle à vide en attendant un changement d’état voire mieux on met en marche un timer qui confirmera l’annulation au bout de 10 secondes, par exemple.

Ah, use, je vois que tu as suivi mes conseils pour le catalogage ! :slight_smile:
Pour le thread, c’est actement ce qu’il faut faire, à un petit détail prêt, peut-être. Je ne suis pas un fan des méthodes stop(), suspend(), etc. (je suppose que c’est ce que tu voulais utiliser en cliquant sur Annuler). C’est peut-être parce qu’elles sont deprecated en Java, que je n’ai pas confiance en elles dans aucun langage. Je préfère placer une variable qui indique l’état et que l’ont lit à chaque occurrence de la boucle dans le thread. Du style : 0 - on continue, 1 - on a appuyé sur Annulé, 2 - Fin du catalogage. Dans les deux derniers cas, on sort tout simplement de la boucle et on met le thread à null.

En plus comme tu places déjà une variable pour indiquer si le catalogage a été annulé ou non, autant t’en servir à fond :slight_smile:

ouais, bah moi je persiste a dire que ca revient a utiliser un bazooka pour gauler un moustique… : oui ca marche, mais oui y a plus simple :slight_smile:

Après quelques essais, je n’ai pas pu trouver l’équivalent du fenetre::idle en C#… J’ai peut être mal cherché ?

Toujours est-il que je me suis intéressé aux threads et en fouillant plus dans l’aide, j’ai ébauché une solution qui ressemble à ça :

  • je crée ma fenêtre en modale
  • je crée un thread qui encapsule la fonction de cataloguage
  • dans cette fonction de cataloguage, je teste l’état d’une variable dans la boucle principale
  • la fonction étant encapsulée dans un thread, normalement je dois avoir la main sur l’interface utilisateur, et donc ça met permet d’avoir accès au bouton annuler
  • le bouton annuler suspend le thread, affiche une messagebox de confirmation, renseigne ou non la variable et relance le thread.
  • La fonction trouve la variable changée dans la boucle, annule l’opération et opère le processus inverse (on vire les fichiers déjà catalogués) sans poser de question.

J’ai codé un début de cette fonction grâce aux classes Thread et ThreadStart mais n’ai pas encore pu tester ça à cause de certains changement de données en base et de quelques erreurs de logique.
Si ça fonctionne, je vous donne les codes exacts en exemple…

PS : la notion de “code propre” est super subjective.
Par exemple, vous préférez déclarer les variables locales en début de procédure ou dynamiquement par le biais de nomvariable = new typevariable ?
Vous préférez utiliser des accolades pour une ligne simple de code ou la mettre à la suite ?
Je préfère largement utiliser :
if(condition)
{
instructions();
}
que if(condition) instructions();
mais c’est un choix personnel. Je trouve que ça participe à la lisibilité du code mais encore une fois c’est une préférence personnelle.
Autre question : vous utilisez des normes d’appellation de variable ?

perso, si qq1 sait faire un thread en c#, je suis preneur, moi je sais pas faire.
ensuite, pour use, en supposant que tu connaisse la syntaxe pour faire un thread (je crois qu’il suffit de mettre une classe serializable, j’vais me renseigner), il faudra :

  1. que ta fenetre d’annulation soit créée par un thread (comme ca, elle bloque pas le reste du processus)

  2. que ton traitement de cataloguage verifie regulierement l’état d’une variable (on va pas faire un delete sur le thread qui fait le cataloguage, c’est pas propre du tout beurk :wink: ), donc ca, tu peux pas y couper…

edit : comment faire du thread en c#, la réponse ici

[Edité le 15/1/2003 par urdle]

Le code n’est pas parfaitement propre mais c’est un bon début :na:
:wink:

[quote]Tiens rigolo, sur le site fourni par KiniK les exemples montrent des accolades ouvrantes seules sur les lignes. Une écriture propre quoi :wink:
Hop favori.[/quote]Héhéhé, salaud :frowning: Je te prends au mot alors : :open_mouth:

[quote][…]
String fileName = openFileDialog1.FileName;
// Since we want to open only xml files hence the condition.

if ( (fileName.Length != 0) && (fileName.EndsWith(« xml »)))
{
[…][/quote]Donc une écriture propre colle la ligne de commentaire en-dessous d’un bout qui n’a rien à voir et saute une ligne avant le code auquel elle se réfère. En plus le commentaire est hyper-intéressant. On n’aurait pas deviné en lisant le test [OK, OK, après relecture mea culpa : je putise, je putise, mais c’est normal ici, c’est un site pour apprendre, et on n’est pas censé savoir relire du code…] :smiley: Mais comme c’est du code propre… :wink:

[quote][…]
while ( req_queue.isEmpty()!=1)
//Wait for space …If Queue is full then we can’t add element to it
{
[…][/quote]On ne parlera pas de la pertinence du commentaire :smiley: [ humhum]. Mais son emplacement doit sûrement être idéal, vu qu’il s’agit de code propre :smiley:

[quote] m_bAbort = false;
QueueMonitorThread = new Thread( new ThreadStart(QueueMonitorfunc));
QueueMonitorThread.Start();[/quote]Hop là, on oublie l’indentation. Pas grave, c’est du code propre :wink:

Tu ne disais pas, il y a quelque temps, que ce n’était pas parce qu’on respectait l’une des 2 conventions discutées que l’on faisait automatiquement du code propre… :na: Nanananère…

[Edit Voilà ce qui arrive quand on tape avec des moufles ;)]
Et je te parle même pas de la succession de if qui avec TA convention ne ressort pas bien vu qu’il y autant de vide que de lignes intéressantes… Tandis qu’avec la mienne, la condition et l’affectation étant collées, car l’accolade ouvrante se retrouve en fin de ligne de la condition, hop ça ressort bien mieux… :smiley: :smiley: :smiley: :cool: hihihi /me est fourbe

[Edité le 15/1/2003 par xentyr]