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. #1
    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 Interrompre un thread
    J'essaye d'utiliser un thread dans une Windows Forms, mais je rencontre un problème lorsque j'utilise la croix pour fermer la fenêtre avant la fin du thread. Le thread ne semble pas s'interrompre correctement.
    Cela me lance une exception : 'Object Disposed Exception' cannot access a dispoded object

    Merci de votre aide.

    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
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
     
         public partial class Form1 : Form
        {
     
            private bool thread_stop = false;
            Thread t=null;
            public delegate void deleg(string x);
     
            private void button1_Click(object sender, EventArgs e)
            {
                t = new Thread(new ThreadStart(le_thread));
                thread_stop = false;
                t.Start();
            }
     
            public void le_thread()
            {
                Random r = new Random();
                int res = 0;
                for (int i = 1; i < 1000; i++)
                {
                    if (thread_stop) { return; }
     
                    Invoke((deleg)maj_txt, i.ToString());
     
                    for (int j = 1; j < 1000; j++)
                    {
                        for (int k = 1; k < 1000; k++)
                        {
                            res = k + r.Next();
                        }
                    }
                }
            }
     
            public void maj_txt(string x)
            {
                info.Text = x;
            }
     
            private void Form1_FormClosing(object sender, FormClosingEventArgs e)
            {
                thread_stop = true;
                if(t!=null)
                    if (t.IsAlive || t.ThreadState == System.Threading.ThreadState.Running)
                    {
                        t.Join(1000);
                    }
     
            }
        }

  2. #2
    Membre régulier
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2012
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2012
    Messages : 64
    Points : 78
    Points
    78
    Par défaut
    Bonjour,

    D'après ce que j'ai lu, normalement les Threads se ferment automatiquement. Donc es-tu sûr que c'est ton Thred qui pose un problème.
    Deuxièmement, es-tu sûr de cette syntaxe :
    En regardant la méthode sur le MSDN, on dirait que ta méthode n'attend pas d'attribut.

    Cordialement,
    Clemou01
    Mon message t'a aidé ? N'hésite pas à cliquer sur . Si ton problème est résolu, clique sur Mon Site Web

  3. #3
    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
    Bonjour Clemou01,

    Mon problème vient du fait que je veux interrompre le thread lors de l'évènement 'FormClosing' qui est déclenché lorsque l'on clique sur la croix rouge de la fenêtre, si je le stop à un autre moment ça marche.
    Pour le Join j'ai essayé sans argument la fenêtre ne se ferme pas et reste bloquée.

    Merci

  4. #4
    Membre chevronné
    Avatar de PixelJuice
    Homme Profil pro
    Ingénieur .NET & Game Designer
    Inscrit en
    Janvier 2014
    Messages
    639
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Ingénieur .NET & Game Designer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Janvier 2014
    Messages : 639
    Points : 2 148
    Points
    2 148
    Par défaut
    Join() n'est pas vraiment fait pour ça , essaye plutôt ça :


  5. #5
    Membre habitué
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    93
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2006
    Messages : 93
    Points : 169
    Points
    169
    Par défaut
    Salut,

    Le soucis se trouve dans ta méthode Invoke() je pense : lorsque tu cliques sur la croix pour fermer l'application, la textbox est "disposed", et lorsque tu veux y affecter ta valeur il te balance l'exception.

    Si j'étais toi je ne m'embêterais pas avec les threads : regarde du côté du BackgroundWorker.

  6. #6
    Invité
    Invité(e)
    Par défaut
    Bonjour,
    Ton idée de fermer proprement le thread est une bonne idée. Après, il y a 2 soucis qui sont combinés : dans ta grosse boucle, tu testes ta variables "thread_stop" à un niveau trop haut, du coup, lorsque tu fais le "Join", tu n'attends qu'une seconde et tu sors tout de suite... Pendant ce temps, dans le thread, avec "Invoke" tu tentes d'accéder à un contrôle qui n'existe plus.
    La solution rapide dans ce cas, consiste à déplacer le test du "thread_stop" à un niveau plus bas dans l'imbrication des boucles :
    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
    public void le_thread()
            {
                Random r = new Random();
                int res = 0;
                for (int i = 1; i < 1000; i++)
                {
     
                    Invoke((deleg)maj_txt, i.ToString());
     
                    for (int j = 1; j < 1000; j++)
                    {
                        for (int k = 1; k < 1000; k++)
                        {
                            if (thread_stop) { return; }
                            res = k + r.Next();
                        }
                    }
                }
            }
    Après, il va faire les tests plus souvent mais bon...

    Moi je te conseille d'utiliser la classe "Task" à la place de la classe Thread...

    Au passage, petit commentaire sur les autres réponses
    Citation Envoyé par clemou01 Voir le message
    Bonjour,

    D'après ce que j'ai lu, normalement les Threads se ferment automatiquement. Donc es-tu sûr que c'est ton Thred qui pose un problème.
    Deuxièmement, es-tu sûr de cette syntaxe :
    En regardant la méthode sur le MSDN, on dirait que ta méthode n'attend pas d'attribut.

    Cordialement,
    Clemou01
    Tu as râté la surcharge avec un entier ici. La surcharge de thomas existait bien...

    Citation Envoyé par thomas55 Voir le message
    Pour le Join j'ai essayé sans argument la fenêtre ne se ferme pas et reste bloquée.
    Oui, en effet pour la raison évoquée plus haut...

    Citation Envoyé par PixelJuice Voir le message
    Join() n'est pas vraiment fait pour ça , essaye plutôt ça :

    Ah oui, ça fonctionne pour cette situation mais c'est très violent et ce n'est plus recommandé par Microsoft. Dans ce cas-ci, ça ne pose pas de problème mais si on est dans un contexte d'écriture dans un fichier ou une base de données par exemple cela risque de compromettre l'intégrité des données.
    Encore une fois, la classe "Task" et le "Task-based Asynchronous Pattern" sont recommandés dans tous les cas, les classes "Thread" et "BackgroundWorker" sont hasbeen. Le "TAP" permet d'inclure un jeton d'annulation permettant d'indiquer juste à la tâche qu'on demande son arrêt mais c'est la tâche qui va décider quel est le moment le plus opportun pour s'arrêter.

    Citation Envoyé par Jaco67 Voir le message
    Si j'étais toi je ne m'embêterais pas avec les threads : regarde du côté du BackgroundWorker.
    BackgoundWorker est hasbeen maintenant. Voici le lien que je montrerai plutôt :
    http://msdn.microsoft.com/fr-fr/libr...v=vs.110).aspx

  7. #7
    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
    Pour ObjectDisposedException le plus simple est de de l'attraper avec un catch et de l'ignorer, c'est sans conséquences. Comme expliqué par Ph_Gr, ça se produit quand le_thread reprend la main après l'expiration du timeout de Join.


    Maintenant ta procédure d'arrêt ne fonctionne pas correctement, on a un cas classique de deadlock dans certaines situations : le_thread attend l'UI avec Invoke, tandis que l'UI attend le_thread avec Join. Je recommande d'utiliser plutôt BeginInvoke. Si ça ne te convient pas parce que tu ne veux pas commencer tes deux boucles imbriquées avant la mise à jour de l'UI, alors il va falloir ruser : soit utiliser une continuation (maj_txt aura la tâche d'invoquer la suite du code sur un autre thread que l'UI - de préférence avec le couple async/await pour plus de clarté), soit un autoresetevent avec timeout sur l'attente.

    Autre chose, une seconde sera sans doute suffisant la plupart du temps mais réfléchis à la façon dont tu veux que le programme se comporte sur une vielle machine avec plusieurs programmes en cours d'exécution se partageant avidement la carcasse du CPU.

  8. #8
    Membre chevronné
    Avatar de PixelJuice
    Homme Profil pro
    Ingénieur .NET & Game Designer
    Inscrit en
    Janvier 2014
    Messages
    639
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Ingénieur .NET & Game Designer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Janvier 2014
    Messages : 639
    Points : 2 148
    Points
    2 148
    Par défaut
    Ah oui, ça fonctionne pour cette situation mais c'est très violent et ce n'est plus recommandé par Microsoft
    Oui dans son cas , si il faudrait répondre de manière générale tout le temps , on aurait pas fini.Je n'ai jamais vu que Abort() était déprécié. (je suis toujours en 4.0) , ou alors était-ce de Suspend() que tu parlais ? (qui lui est déprécié).

    Encore une fois, la classe "Task" et le "Task-based Asynchronous Pattern" sont recommandés dans tous les cas, les classes "Thread" et "BackgroundWorker" sont hasbeen
    Si il reste en .NET toute sa vie , oui.

    Sinon je ne conseillerai jamais a quelqu'un d'abandonner un truc aussi élémentaire que les threads.

    Après c'est subjectif , mais je préfère utiliser un truc qui est commun a beaucoup de langage , que d'utiliser des trucs "gadget" comme BackgroundWorker.

  9. #9
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par PixelJuice Voir le message
    Oui dans son cas , si il faudrait répondre de manière générale tout le temps , on aurait pas fini.Je n'ai jamais vu que Abort() était déprécié. (je suis toujours en 4.0) , ou alors était-ce de Suspend() que tu parlais ? (qui lui est déprécié).
    En fait, je n'ai pas dit que c'était déprécié, la raison pour laquelle il ne faut plus l'utiliser je l'ai mis dans mon argumentaire plus haut.

    Citation Envoyé par PixelJuice Voir le message
    Sinon je ne conseillerai jamais a quelqu'un d'abandonner un truc aussi élémentaire que les threads.
    Pour la raison que j'ai évoqué ci-dessus, il est préférable d'utiliser la classe "Task" en .NET...
    Et puisque thomas débute, autant qu'il prenne les bonnes habitudes dès le début!

    Citation Envoyé par PixelJuice Voir le message
    Après c'est subjectif , mais je préfère utiliser un truc qui est commun a beaucoup de langage , que d'utiliser des trucs "gadget" comme BackgroundWorker.
    Tu as raison sur BackgroundWorker, il est en plus assez compliqué d'utilisation. C'est l'ancêtre du "TAP" dont je parlais.
    La classe "Task" n'est pas qu'un simple gadget : elle permet une gestion plus simple de multi-threading. De plus, la classe "Task" montre la bonne pratique sur l'utilisation de Thread: pas de "Abort" direct, mais une demande d'arrêt à la place.
    C'est bien plus propre!

    Après, c'est vous qui voyez!...

  10. #10
    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 PixelJuice Voir le message
    Oui dans son cas , si il faudrait répondre de manière générale tout le temps , on aurait pas fini.
    Même dans son cas Abort n'est pas recommandé : imagine qu'il survienne lors de l'appel à Invoke, il pourrait alors corrompre l'état de la file d'attente de l'UI et bloquer la suite de l'application (ce qui peut être problématique même à la fermeture).

    Concernant le BackgroundWorker il est en effet complètement inutile aujourd'hui avec async/await. Concernant Thread, celui-ci a encore tout son intérêt même si, pour de nombreux scénarios, il a toujours été recommandé de passer plutôt par le ThreadPool (ce que font Task.Run et bien d'autres).

  11. #11
    Invité
    Invité(e)
    Par défaut
    Oui, c'est vrai, j'y ai été un peu fort d'employer le terme "hasbeen" pour la classe "Thread", car elle est quand même utilisée pour la cuisine interne de la classe "Task" avec l'option "TaskCreationOptions.LongRunning". D'ailleurs, d'après ce que j'ai vu dans le code, le "Scheduler" par défaut n'utilises la classe "Thread" qu'avec l'option "IsBackground" à true.

  12. #12
    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
    Oui, c'est vrai, j'y ai été un peu fort d'employer le terme "hasbeen" pour la classe "Thread", car elle est quand même utilisée pour la cuisine interne de la classe "Task" avec l'option "TaskCreationOptions.LongRunning". D'ailleurs, d'après ce que j'ai vu dans le code, le "Scheduler" par défaut n'utilises la classe "Thread" qu'avec l'option "IsBackground" à true.
    Surtout Thread est à utiliser dans bien des cas :
    * Il faut un contrôle manuel des priorités, un thread STA ou un thread d'avant-plan.
    * Il faut une identité stable : acteurs, objets avec une affinité pour un thread donné (UI), etc.
    * L'opération exécutée a de longues pauses, ce qui pourrait entraîner une pénurie de threads actifs dans le pool.

  13. #13
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par DonQuiche Voir le message
    * Il faut un contrôle manuel des priorités, un thread STA ou un thread d'avant-plan.
    Oui, en effet, l'un des points faibles de la classe Task c'est de ne pas pouvoir préciser la priorité sans avoir à se farcir la création d'une nouvelle dérivée de la classe "TaskScheduler".

    Citation Envoyé par DonQuiche Voir le message
    * Il faut une identité stable : acteurs, objets avec une affinité pour un thread donné (UI), etc.
    * L'opération exécutée a de longues pauses, ce qui pourrait entraîner une pénurie de threads actifs dans le pool.
    Pour ces deux cas, normalement l'utilisation de la classe Task avec l'option "TaskCreationOptions.LongRunning" peux faire l'affaire.

    Cela dit, pour le cas de notre ami Thomas, la classe Task lui suffit largement je penses, non?

  14. #14
    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
    Pour ces deux cas, normalement l'utilisation de la classe Task avec l'option "TaskCreationOptions.LongRunning" peux faire l'affaire.
    J'avoue que je ne m'étais jamais penché sur cette option et je te remercie. Cela dit si tu n'as pas l'intention d'utiliser async/await (j'imagine que ton thread démarre avec un SyncrhonizationContext dédié et sa propre file d'attente) et autres formes de séquençage de tâches je ne vois pas l'intérêt de passer par Task plutôt que par Thread même si je reconnais que la première peut lui être substitué.

    Les tâches sont parfaites pour ce pour quoi elles sont faîtes. Mais je ne vois pas de raison d'en faire une solution universelle quand un thread fait très bien l'affaire, autant éviter une surcouche.

    Cela dit, pour le cas de notre ami Thomas, la classe Task lui suffit largement je penses, non?
    S'il compte utiliser BeginInvoke, les tâches n'apporteraient rien. Puisque le code est écrit, autant le garder.

    S'il compte conserver cette synchronisation il doit la refaire et dans ce cas une tâche serait plus indiquée.

  15. #15
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par DonQuiche Voir le message
    S'il compte utiliser BeginInvoke, les tâches n'apporteraient rien. Puisque le code est écrit, autant le garder.
    Eh bien ça apporterait au moins la gestion de l'annulation :

    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
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    public partial class Form1 : Form
    	{
    		/// <summary>
    		/// Tâche principale
    		/// </summary>
    		Task _t = null;
     
    		/// <summary>
    		/// Source d'annulation
    		/// </summary>
    		CancellationTokenSource _tks = new CancellationTokenSource();
     
    		public Form1()
    		{
    			InitializeComponent();
    		}
     
    		private void button1_Click(object sender, EventArgs e)
    		{
    			if (_t == null || _t.IsCompleted)
    				_t = Task.Factory.StartNew(le_thread, TaskCreationOptions.LongRunning);
    			else
    			{
    				System.Diagnostics.Debug.WriteLine("La tâche est en cours d'exécution, on ne va pas la lancer une seconde fois.");
    			}
    		}
     
    		public void le_thread()
    		{
    			Random r = new Random();
    			int res = 0;
    			for (int i = 1; i < 1000; i++)
    			{
    				this.BeginInvoke((Action)delegate() { info.Text = i.ToString(); });
     
    				for (int j = 1; j < 1000; j++)
    				{
    					for (int k = 1; k < 1000; k++)
    					{
    						res = k + r.Next();
     
    						//Lance l'exception "OperationCancelledExcpetion" pour annuler la tâche
    						//si la source d'annulation le demande.
    						_tks.Token.ThrowIfCancellationRequested();
    					}
    				}
    			}
    		}
     
    		private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    		{
    			if (_t != null)
    			{
    				//La source d'annulation va demander l'arrêt de la tâche
    				_tks.Cancel();
     
    				//Attendre la fin de la tâche.
    				try { _t.Wait(); }
    				catch (AggregateException aggrExc)
    				{
    					//C'est l'inconvéniant du "Wait" : il encapsule l'exception avec un AggregateException
    					if (aggrExc.InnerException is OperationCanceledException)
    					{
    						System.Diagnostics.Debug.WriteLine("La tâche a été annulée");
                                                   //Ici, _t.IsCancelled vaut true
    					}
    					else
    					{
    						System.Diagnostics.Debug.WriteLine("Une autre erreur a eu lieu dans la tâche.");
                                                    //Ici, _t.IsFaulted vaut true
    					}
    				}
     
    				//Ici, ça c'est fini proprement normalement. Dans tous les cas, ici _t.IsCompleted vaut true
    			}
    		}
    	}
    Bon alors c'est vrai, il y a un gros défaut dans la classe Task : quand on fait "Wait", les exceptions sont encapsullées dans un "AggregateException" même s'il n'y en a qu'une...

    Alors autre chose aussi : bien penser à aller dans "Debugguer / Exceptions" puis sous "Common language runtime exceptions" et enlever la coche de "System.OperationCancelledException" sinon ça va péter en mode débug mais ce ne sera pas bloquant, on peut faire "continuer" quand ça pête car c'est quand même attrapé.

    Nom : Desactiver_exception_operationcancelled.png
Affichages : 474
Taille : 19,5 Ko

    Voilà.

  16. #16
    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
    Eh bien ça apporterait au moins la gestion de l'annulation :
    Pas spécialement : tu as simplement remplacé son booléen (qu'il faudrait marquer volatile) par un CTS, son thread.Join par un Task.Wait, et sa clause de garde (plus un catch manquant dans maj_txt) par un appel de méthode lançant éventuellement une exception avec toute une plomberie nécessaire en aval. A mes yeux c'est moins avantageux.

    Par ailleurs seul Task.Wait et la remontée de l'exception sont spécifiques aux tâches ici, les CTS existaient avant les tâches.

  17. #17
    Invité
    Invité(e)
    Par défaut
    OK DonQuiche, admettons.
    Dans ce cas, peux-tu montrer l'équivalent en utilisant juste la classe Thread?

    [EDIT] Mince, et en plus j'avais oublié de passer le token en argument à "StartNew", du coup sans ça il ne passera pas à l'état "Cancelled". Je vais reposter des sources de cela avec des améliorations...
    Dernière modification par Invité ; 02/04/2014 à 08h24.

  18. #18
    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
    Hello,

    Vous partez bien vite dans des algos alambiqués.

    Ph_Gr, le fait de remonter this.BeginInvoke((Action)delegate() { info.Text = i.ToString(); }); ne fait que diminuer le risque que ce soit appelé alors que la form est disposée, ça n'est pas thread-safe.
    [Edit] J'avais lu trop vite, avec CancellationTokenSource, à priori c'est thread-safe
    [Edit2] J'ai trouvé, c'est la 1ère solution que tu avais postée qui n'était pas thread-safe, avec : Invoke((deleg)maj_txt, i.ToString());

    De plus, il est préférable que la méthode qui fait les calculs (nommée non conventionnellement "le_thread()") reste agnostique de l'IHM.

    Le junior trouve une solution compliquée à un problème simple ; le senior trouve une solution simple à un problème compliqué

    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
    21
    22
    23
    24
    25
    26
    27
    28
    29
    private async void button1_Click(object sender, EventArgs e)
    {
    	button1.Enabled = false;
    	await DoWork();
    	button1.Enabled = true;
    }
     
    public async Task DoWork()
    {
    	IProgress<int> progress = new Progress<int>(i => ReportProgress(i));
    	await Task.Run(() =>
    	{
    		Random r = new Random();
    		int res = 0;
    		for (int i = 1; i < 1000; i++)
    		{
    			progress.Report(i);
     
    			for (int j = 1; j < 1000; j++)
    				for (int k = 1; k < 1000; k++)
    					res = k + r.Next();
    		}
    	});
    }
     
    private void ReportProgress(int i)
    {
    	textBox1.Text = i.ToString();
    }

  19. #19
    Invité
    Invité(e)
    Par défaut
    Bonjour theonlymax,

    Petite précision au sujet du "BeginInvoke" : avec le "barrage" mis dans le "closing" cela me garantit que mon contrôle existe toujours. Sinon, tu aurais exactement le même soucis avec ta solution.
    Cela dit, tu as raison sur le principe de séparation avec l'IHM.

    Mais dans ta solution tu oublies 2 choses essentielles :
    - Ta solution va utiliser le thread pool et on souhaiterait utiliser plutôt un vrai Thread.
    - Ta solution ne comprend pas de gestion d'annulation propre.

    Je m'apprêtais à mettre ma source à ce sujet :
    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
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    partial class Form1 : Form
    	{
    		/// <summary>
    		/// Tâche principale
    		/// </summary>
    		Task _t = null;
     
    		/// <summary>
    		/// Source d'annulation
    		/// </summary>
    		CancellationTokenSource _tks = new CancellationTokenSource();
     
    		public Form1()
    		{
    			InitializeComponent();
    		}
     
    		private async void button1_Click(object sender, EventArgs e)
    		{
    			if (_t == null || _t.IsCompleted)
    			{
    				Traitements traitements = new Traitements();
    				_t = traitements.le_thread(_tks.Token, new Progress<int>(i => { info.Text = i.ToString(); }));
    				button1.Enabled = false;
    				try { await _t; }
    				catch { }
    				finally { button1.Enabled = true; }
    			}
    			else
    			{
    				System.Diagnostics.Debug.WriteLine("La tâche est en cours d'exécution, on ne va pas la lancer une seconde fois.");
    			}
    		}
     
    		private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    		{
    			if (_t != null)
    			{
    				//La source d'annulation va demander l'arrêt de la tâche
    				_tks.Cancel();
     
    				//Attendre la fin de la tâche.
    				try { _t.Wait(); }
    				catch (AggregateException aggrExc)
    				{
    					//C'est l'inconvéniant du "Wait" : il encapsule l'exception avec un AggregateException
    					if (aggrExc.InnerException is OperationCanceledException)
    					{
    						System.Diagnostics.Debug.WriteLine("La tâche a été annulée");
    					}
    					else
    					{
    						System.Diagnostics.Debug.WriteLine("Une autre erreur a eu lieu dans la tâche.");
    					}
    				}
    				System.Diagnostics.Debug.WriteLine("Statut de la tâche : {0}", _t.Status);
    				//Ici, ça c'est fini proprement normalement.
    			}
    		}
    	}

    Et ma classe "Traitements" :
    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
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public class Traitements
    	{
    		public Task le_thread(CancellationToken cancelToken, IProgress<int> progress)
    		{
    			return Task.Factory.StartNew(() => Implementation_le_thread(cancelToken,progress), cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Default);
    		}
     
    		private void Implementation_le_thread(CancellationToken cancelToken, IProgress<int> progress)
    		{
    			Random r = new Random();
    			int res = 0;
    			for (int i = 1; i < 1000; i++)
    			{
    				if (progress != null) progress.Report(i);
     
    				for (int j = 1; j < 1000; j++)
    				{
    					for (int k = 1; k < 1000; k++)
    					{
    						res = k + r.Next();
     
    						//Lance l'exception "OperationCancelledExcpetion" pour annuler la tâche
    						//si la source d'annulation le demande.
    						cancelToken.ThrowIfCancellationRequested();
    					}
    				}
    			}
    		}
    	}

    Donc là l'intérêt est évidemment :
    - Séparation du traitement par rapport à l'IHM.
    - Gestion d'annulation

    Là, j'applique vraiment le TAP. (en espérant que je n'ai rien oublié ce coup-ci)

  20. #20
    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
    Pourquoi n'utilises-tu pas un backgroundworker ?
    Ensuite tu fais
    <tonthread>.sleep(time);

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

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