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

C# Discussion :

Interrompre un thread [Débutant]


Sujet :

C#

  1. #21
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    26
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 26
    Points : 89
    Points
    89
    Par défaut
    Ph_Gr,

    J'ai corrigé le coup du thread-safe plus haut, dsl.

    Pour te répondre :
    - La gestion de l'annulation est, dans ma solution, implicitement gérée par le framework. A moins que thomas55 veuille sauvegarder quelquechose à l'annulation de son calcul, ça convient très bien.
    - Pour ce qui est de court-circuiter le thread pool, quelle en est la raison ? C'est une très mauvais pratique. Je ne crois pas utiliser un "faux" thread... Ce que cherche à faire thomas55, c'est un long calcul sans freezer l'IHM, donc lancer un thread en background.

    Pourquoi réinventer la roue ? Tu veux faire plus propre que les classes existantes du framework ?

    @thomas55, ma solution te convient-elle ?

  2. #22
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par TheOnlyMaX Voir le message
    - La gestion de l'annulation est, dans ma solution, implicitement gérée par le framework. A moins que thomas55 veuille sauvegarder quelquechose à l'annulation de son calcul, ça convient très bien.
    Eh bien oui, il faut partir du principe que c'est ce qu'il voudra faire ensuite.

    Citation Envoyé par TheOnlyMaX Voir le message
    - Pour ce qui est de court-circuiter le thread pool, quelle en est la raison ? C'est une très mauvais pratique. Je ne crois pas utiliser un "faux" thread... Ce que cherche à faire thomas55, c'est un long calcul sans freezer l'IHM, donc lancer un thread en background.
    Oui, je me suis un peu mal exprimé en disant "vrai thread"; quand je spécifie l'option "LongRunning" cela fait un appel derrière à une instance de la classe "Thread" alors que sans cet argument on fait appel à "ThreadPool.QueueUserWorkItem". Si tu regardes le code du TaskScheduler par défaut qui s'appelle "ThreadPoolTaskScheduler", le code derrière donne ceci :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    [SecurityCritical]
            protected internal override void QueueTask(Task task)
            {
                if ((task.Options & TaskCreationOptions.LongRunning) != TaskCreationOptions.None)
                {
                    Thread thread = new Thread(ThreadPoolTaskScheduler.s_longRunningThreadWork)
                    {
                        IsBackground = true
                    };
                    thread.Start(task);
                    return;
                }
                bool options = (task.Options & TaskCreationOptions.PreferFairness) != TaskCreationOptions.None;
                ThreadPool.UnsafeQueueCustomWorkItem(task, options);
            }

    Lorsque la tâche est longue et qu'elle risque de faire plein de pauses, il vaut mieux ne pas passer par le ThreadPool. Donc la solution que j'ai proposé correspond mieux à l'algorithme d'origine de Thomas.

    Citation Envoyé par TheOnlyMaX Voir le message
    Pourquoi réinventer la roue ? Tu veux faire plus propre que les classes existantes du framework ?
    Où tu as vu que je réinventais la roue? Là il faut que tu m'expliques je ne comprends pas.

  3. #23
    Expert confirmé Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Points : 5 485
    Points
    5 485
    Par défaut
    Citation Envoyé par Ph_Gr Voir le message
    OK DonQuiche, admettons.
    Dans ce cas, peux-tu montrer l'équivalent en utilisant juste la classe Thread?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
            volatile bool thread_stop = false; // utilisation de volatile
     
            void le_thread()
            {
                 BeginInvoke((deleg)maj_txt, i.ToString());
            }
     
            public void maj_txt(string x)
            {
     
                 try {   info.Text = x;  } catch (ObjectDisposedException) {}
            }
        }
    Soit un total de moins de 40 caractères modifiés. Est-ce que c'est la solution la plus belle ? Non. Est-ce qu'elle corrige les problèmes ? Oui.

  4. #24
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par DonQuiche Voir le message
    Soit un total de moins de 40 caractères modifiés. Est-ce que c'est la solution la plus belle ? Non. Est-ce qu'elle corrige les problèmes ? Oui.
    OK. Bon alors ce fil de discussion commence à devenir un peu long. Je pourrais encore ajouter un argumentaire supplémentaire à cela (argumentaire déjà discuté avec TheOnlyMax d'ailleurs) mais je pense que l'on devrais s'arrêter ici.
    Ce que je voulais surtout souligner c'est que pour faire du multitâche, il n'y a pas que la classe "Thread", il y a aussi la classe "Task". Après, je reconnais que je me suis un peu laissé emporter en ce qui concerne la classe "Thread" et que je l'avais "lynché" un peu vite (c'est tout écrit et je ne peut pas le nier).
    C'est vrai que si on veut faire un petit exemple tout bête comme cela, la classe "Thread" est très bien comme tu le dit, mais si on veut aller un peu plus loin, avoir plus de possibilités d'évolutions et comme le soulignais si bien TheOnlyMax
    que la méthode qui fait les calculs ... reste agnostique de l'IHM
    que j'approuve à 100%, il convient mieux d'utiliser la classe Task et le TAP.

  5. #25
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    26
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 26
    Points : 89
    Points
    89
    Par défaut
    Merci Ph_Gr pour l'explication sur l'option "LongRunning", je ne connaissais pas.
    Par contre, il faut l'utiliser en connaissance de cause, de préférence uniquement dans le cas où le fait d'utiliser le ThreadPool pose un réel problème.

    Citation Envoyé par Ph_Gr Voir le message
    Eh bien oui, il faut partir du principe que c'est ce qu'il voudra faire ensuite.
    Non, il ne faut pas partir du principe que c'est peut-être ce qu'il voudra faire. L'énoncé est l'énoncé !
    Mais, admettons qu'avec un peu de chance, tu as raison

  6. #26
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par TheOnlyMaX Voir le message
    Par contre, il faut l'utiliser en connaissance de cause, de préférence uniquement dans le cas où le fait d'utiliser le ThreadPool pose un réel problème.
    Oui tout à fait comme le disait aussi DonQuiche.

    Citation Envoyé par TheOnlyMaX Voir le message
    Non, il ne faut pas partir du principe que c'est peut-être ce qu'il voudra faire. L'énoncé est l'énoncé !
    Mais, admettons qu'avec un peu de chance, tu as raison
    Seul thomas pourra nous le dire...

  7. #27
    Membre régulier
    Homme Profil pro
    Apprenti Ingénieur Informatique
    Inscrit en
    Octobre 2013
    Messages
    82
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Apprenti Ingénieur Informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2013
    Messages : 82
    Points : 84
    Points
    84
    Par défaut
    BackgoundWorker est hasbeen maintenant. Voici le lien que je montrerai plutôt
    Sur quoi te bases tu pour affirmer cela ?

  8. #28
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par benobab Voir le message
    Sur quoi tu bases tu pour affirmer cela ?
    Sur son usage, tout simplement.
    En fait, quand on a un "BackgroundWorker" :
    - il faut le paramétrer en disant "qu'est-ce que je vais mettre dans mon "DoWork", qu'est ce que je vais mettre dans mon "RunWorkerCompleted", dans mon "ProgressChanged" et je trouve cette phase vraiment lourde... en tous cas plus lourde qu'en utilisant des Tasks.
    - Je ne vois pas comment l'inclure dans un scénario de séparation IHM/logique métier (essaye de faire la même chose qu'avec mon code). Il faut dire que le composant avait été conçu à une période ou cette séparation n'était pas du tout une préoccupation.
    - Task a aussi une version générique où elle renvoie un résultat. Avec un BackgroundWorker il faudrait stocker ce résultat quelque part en variable membre... Pas terrible!

    et puis, citation de la page MSDN sur la programmation asynchrone :
    ...
    The async-based approach to asynchronous programming is preferable to existing approaches in almost every case. In particular, this approach is better than BackgroundWorker for IO-bound operations because the code is simpler and you don't have to guard against race conditions. In combination with Task.Run, async programming is better than BackgroundWorker for CPU-bound operations because async programming separates the coordination details of running your code from the work that Task.Run transfers to the threadpool.
    A l'époque le BackgroundWorker était très bien, il fait bien son job, pas de pb. Mais au niveau des pratiques async/await est plus simple et permet la séparation IHM/Logique métier plus facilement.

  9. #29
    Membre régulier
    Homme Profil pro
    Apprenti Ingénieur Informatique
    Inscrit en
    Octobre 2013
    Messages
    82
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Apprenti Ingénieur Informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2013
    Messages : 82
    Points : 84
    Points
    84
    Par défaut
    D'accord je vois. Mais sache que tu peux remplir simplement de Dowork du backgroundWorker, car dans mes projets je n'utilise que cette partie du backgroundworker.

    En tout cas je prends note alors car je travail seulement en multithread

  10. #30
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    26
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 26
    Points : 89
    Points
    89
    Par défaut
    Citation Envoyé par Ph_Gr Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    try { await _t; }
    catch { }
    Masquer une exception est quand même une des pires pratiques qui existe. N'essayez pas de faire pareil à la maison !

    Citation Envoyé par Ph_Gr Voir le message
    Ta solution ne comprend pas de gestion d'annulation propre.
    Le problème évoqué par thomas55 intervient à la fermeture de l'application. Les threads background sont terminés lorsque le thread de l'UI se termine. Il n'y a donc pas besoin de gestion d'annulation explicite. Une usine à gaz inutile ne sera pas plus propre.

    Le programme donné par thomas55 peut être simplement corrigé en replaçant Invoke par BeginInvoke, et en mettant le thread en background. On s'affiranchit alors de la gestion de l'annulation, qui existait uniquement parce que la textbox était disposée au moment de la mettre à jour car le thread continuait à s'exécuter après fermeture de la form.
    Ainsi, le programme devient :
    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
    private void btnDoWork_Click(object sender, EventArgs e)
    {
    	Thread t = new Thread(new ThreadStart(DoWork));
    	t.IsBackground = true;
    	t.Start();
    }
     
    public void DoWork()
    {
    	Random r = new Random();
    	int res = 0;
    	for (int i = 1; i < 1000; i++)
    	{
    		BeginInvoke((Action)(() => textBox1.Text = i.ToString()));
     
    		for (int j = 1; j < 1000; j++)
    			for (int k = 1; k < 1000; k++)
    				res = k + r.Next();
    	}
    }
    Ce qui revient à la solution que j'ai proposée dans ma 1ère réponse, à ceci près qu'ici on n'utilise pas le ThreadPool (ce que je déconseille à priori). C'est aussi plus compliqué en utilisant Thread de désactiver le bouton pour le réactiver à la fin du traitement.

  11. #31
    Invité
    Invité(e)
    Par défaut
    Bon allez, c'est reparti pour un tour.
    Je vais quand même répondre car il y a un point intéressant à préciser quand même concernant l'annulation d'une tâche.

    Citation Envoyé par TheOnlyMaX Voir le message
    Masquer une exception est quand même une des pires pratiques qui existe. N'essayez pas de faire pareil à la maison !
    En effet, celui-là je l'ai fait un peu à la va-vite. J'aurais du faire plutôt :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    try { await _t; }
    catch(OperationCancelledException)
    {
        //Ici normal qu'on ne fasse rien c'est l'exception qui va passer la tâche à l'état "annulé".
    }
    catch(Exception exc)
    {
        //Autre point intéressant : grâce au fait que l'on est revenu dans le bon contexte, pas besoin de faire appel à "BeginInvoke"
        //pour gérer cette exception avec un message.
        MessageBox.Show(this, string.Format("L'erreur suivante a eu lieu : {0}", exc.Message), , MessageBoxButton.OK, MessageBoxImage.Error);
    }
    C'était le point intéressant à préciser.
    A noter aussi que si on voulait bien gérer les exceptions avec un objet Thread, cela aurait été plus compliqué car l'exception ne remonte pas dans le contexte de l'UI! Idéalement, il faudrait procéder ainsi avec le contenu du Thread :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    void le_thread()
    {
       try
       {
           ...
       }
       catch(Exception exc)
       {
          BeginInvoke((Action)(() => MessageBox.Show(this, { string.Format("L'erreur suivante a eu lieu : {0}", exc.Message), , MessageBoxButton.OK, MessageBoxImage.Error); }));
       }
    }
    Citation Envoyé par TheOnlyMaX Voir le message
    Le problème évoqué par thomas55 intervient à la fermeture de l'application. Les threads background sont terminés lorsque le thread de l'UI se termine. Il n'y a donc pas besoin de gestion d'annulation explicite. Une usine à gaz inutile ne sera pas plus propre.
    Oui, ils sont terminés en plein milieu d'un traitement, en effet. C'est pourquoi s'il n'y a pas de données critiques à préserver tu as raison mais en revanche s'il y a une écriture ou une sauvegarde en cours ou quelque chose comme cela mon "usine à gaz" comme tu dis est indispensable. Mais bon tout ça on en a déjà discuté et je ne vois pas pourquoi revenir dessus.

    Citation Envoyé par TheOnlyMaX Voir le message
    Le programme donné par thomas55 peut être simplement corrigé en replaçant Invoke par BeginInvoke, et en mettant le thread en background. On s'affiranchit alors de la gestion de l'annulation, qui existait uniquement parce que la textbox était disposée au moment de la mettre à jour car le thread continuait à s'exécuter après fermeture de la form.
    Cela a déjà été dit par DonQuiche oui, inutile de revenir dessus encore une fois.

    Au fait, je tiens à rappeler quand même que le titre du sujet était : "Interrompre un thread", et je voulais quand même mentionner l'existence de la classe Task et du Task-based asynchronous pattern pour traiter l'arrêt propre d'un thread.

  12. #32
    Membre à l'essai
    Inscrit en
    Septembre 2012
    Messages
    12
    Détails du profil
    Informations forums :
    Inscription : Septembre 2012
    Messages : 12
    Points : 17
    Points
    17
    Par défaut
    Merci à tous pour votre aide, j'ai du mettre les threads de côté pour l'instant pour des choses plus urgentes à faire au travail.

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 2 PremièrePremière 12

Discussions similaires

  1. Interrompre une thread grace au signaux.
    Par code_vx dans le forum C++
    Réponses: 7
    Dernier message: 29/05/2013, 21h49
  2. Thread interrupt, comment interrompre une API tout en libérant les ressources
    Par rpelissi dans le forum Concurrence et multi-thread
    Réponses: 0
    Dernier message: 19/10/2009, 13h30
  3. [GLib] Interrompre un appel bloquant dans un thread
    Par Zorgblub dans le forum Réseau
    Réponses: 3
    Dernier message: 17/11/2007, 18h02
  4. [thread ] interrompre un thread ?
    Par yann_p dans le forum Concurrence et multi-thread
    Réponses: 18
    Dernier message: 27/04/2006, 14h27
  5. [Thread] Interrompre un Thread
    Par Arnaud51 dans le forum Concurrence et multi-thread
    Réponses: 3
    Dernier message: 13/03/2005, 21h41

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