Vous ne pouvez pas. Les tâches utilisent les threads d'arrière-plan du pool de threads. L'annulation des threads à l'aide de la méthode Abort n'est pas recommandée. Vous pouvez consulter le billet de blog suivant qui explique une manière appropriée d'annuler des tâches à l'aide de jetons d'annulation. Voici un exemple:
classProgram{staticvoidMain(){var ts =newCancellationTokenSource();CancellationToken ct = ts.Token;Task.Factory.StartNew(()=>{while(true){// do some heavy work hereThread.Sleep(100);if(ct.IsCancellationRequested){// another thread decided to cancelConsole.WriteLine("task canceled");break;}}}, ct);// Simulate waiting 3s for the task to completeThread.Sleep(3000);// Can't wait anymore => cancel this task
ts.Cancel();Console.ReadLine();}}
Belle explication. J'ai une question, comment ça marche quand nous n'avons pas de méthode anonyme dans Task.Factory.StartNew? comme Task.Factory.StartNew (() => ProcessMyMethod (),
cancelToken
61
que se passe-t-il s'il y a un appel bloquant qui ne retourne pas dans la tâche en cours d'exécution?
mehmet6parmak
3
@ mehmet6parmak Je pense que la seule chose que vous pouvez faire est de l'utiliser Task.Wait(TimeSpan / int)pour lui donner un délai (basé sur le temps) de l'extérieur.
Mark
2
Et si j'ai ma classe personnalisée pour gérer l'exécution des méthodes dans un nouveau Task? Quelque chose comme: public int StartNewTask(Action method). A l' intérieur du StartNewTaskméthode que je crée un nouveau Taskpar: Task task = new Task(() => method()); task.Start();. Alors, comment puis-je gérer le CancellationToken? Je voudrais également savoir si Threadje dois implémenter une logique pour vérifier s'il y a des tâches qui sont toujours en suspens et donc les tuer quand Form.Closing. Avec Threadsj'utilise Thread.Abort().
Cheshire Cat
Omg, quel mauvais exemple! C'est une simple condition booléenne, bien sûr c'est la première que l'on essaierait! Mais c'est-à-dire que j'ai une fonction dans une tâche séparée, qui peut prendre beaucoup de temps pour se terminer, et idéalement, il ne devrait rien savoir sur un threading ou autre. Alors, comment annuler la fonction avec vos conseils?
Hi-Angel
32
L'annulation d'une tâche est facilement possible si vous capturez le thread dans lequel la tâche s'exécute. Voici un exemple de code pour illustrer ceci:
voidMain(){Thread thread =null;Task t =Task.Run(()=>{//Capture the thread
thread =Thread.CurrentThread;//Simulate work (usually from 3rd party code)Thread.Sleep(1000);//If you comment out thread.Abort(), then this will be displayedConsole.WriteLine("Task finished!");});//This is needed in the example to avoid thread being still NULLThread.Sleep(10);//Cancel the task by aborting the thread
thread.Abort();}
J'ai utilisé Task.Run () pour montrer le cas d'utilisation le plus courant pour cela - en utilisant le confort de Tasks avec l'ancien code à un seul thread, qui n'utilise pas la classe CancellationTokenSource pour déterminer s'il doit être annulé ou non.
Merci pour cette idée. Utilisé cette approche pour implémenter un délai d'expiration pour un code externe, qui n'a pas de CancellationTokensupport ...
Christoph Fink
7
AFAIK thread.abort vous laissera inconnu sur votre pile, il peut être invalide. Je ne l'ai jamais essayé mais je suppose que démarrer un thread dans un domaine d'application séparé que thread.abort sera enregistré! De plus, un thread entier est gaspillé dans votre solution juste pour abandonner une tâche. Vous n'auriez pas à utiliser des tâches mais des threads en premier lieu. (
vote négatif
1
Comme je l'ai écrit, cette solution est un dernier recours qui pourrait être envisagé dans certaines circonstances. Bien sûr, une CancellationTokensolution, voire plus simple, sans condition de concurrence, devrait être envisagée. Le code ci-dessus illustre uniquement la méthode, pas le domaine d'utilisation.
Florian Rappl
8
Je pense que cette approche pourrait avoir des conséquences inconnues et je ne la recommanderais pas dans le code de production. Les tâches ne sont pas toujours garanties d'être exécutées dans un thread différent, ce qui signifie qu'elles peuvent être exécutées dans le même thread que celui qui les a créées si le planificateur le décide (ce qui signifie que le thread principal sera passé à la threadvariable locale). Dans votre code, vous pourriez alors finir par abandonner le thread principal, ce qui n'est pas ce que vous voulez vraiment. Peut-être qu'un contrôle si les threads sont les mêmes avant l'abandon serait une bonne idée, si vous insistez pour abandonner
Ivaylo Slavov
10
@Martin - Comme j'ai recherché cette question sur SO, je vois que vous avez voté à la baisse plusieurs réponses qui utilisent Thread.Abort pour tuer des tâches, mais vous n'avez fourni aucune solution alternative. Comment supprimer un code tiers qui ne prend pas en charge l'annulation et qui s'exécute dans une tâche ?
Gordon Bean
32
Comme le suggère cet article , cela peut être fait de la manière suivante:
intFoo(CancellationToken token){Thread t =Thread.CurrentThread;
using (token.Register(t.Abort)){// compute-bound work here}}
Bien que cela fonctionne, il n'est pas recommandé d'utiliser une telle approche. Si vous pouvez contrôler le code qui s'exécute dans la tâche, vous feriez mieux de gérer correctement l'annulation.
+1 pour donner une approche différente tout en indiquant ses replis. Je ne savais pas que cela pouvait être fait :)
Joel
1
Merci pour la solution! Nous pouvons simplement passer le jeton à la méthode et annuler la source de jeton, au lieu d'obtenir d'une manière ou d'une autre l'instance de thread de la méthode et d'abandonner directement cette instance.
Ce genre de chose est l'une des raisons logistiques pour lesquelles Abortest obsolète. D'abord et avant tout, ne pas utiliser Thread.Abort()pour annuler ou arrêter un thread si cela est possible. Abort()ne doit être utilisé que pour tuer de force un thread qui ne répond pas à des demandes plus pacifiques d'arrêter en temps opportun.
Cela étant dit, vous devez fournir un indicateur d'annulation partagé qu'un thread définit et attend pendant que l'autre thread vérifie périodiquement et se termine normalement. .NET 4 comprend une structure spécialement conçue à cet effet, le CancellationToken.
Vous ne devriez pas essayer de faire cela directement. Concevez vos tâches pour qu'elles fonctionnent avec un CancellationToken et annulez-les de cette façon.
De plus, je recommanderais de changer votre thread principal pour qu'il fonctionne également via un CancellationToken. L'appel Thread.Abort()est une mauvaise idée - cela peut entraîner divers problèmes qui sont très difficiles à diagnostiquer. Au lieu de cela, ce thread peut utiliser la même annulation que vos tâches - et la même chose CancellationTokenSourcepeut être utilisée pour déclencher l'annulation de toutes vos tâches et de votre thread principal.
Cela conduira à une conception beaucoup plus simple et plus sûre.
Pour répondre à la question de Prerak K sur la façon d'utiliser CancellationTokens lorsque vous n'utilisez pas une méthode anonyme dans Task.Factory.StartNew (), vous passez le CancellationToken en tant que paramètre dans la méthode que vous démarrez avec StartNew (), comme indiqué dans l'exemple MSDN ici .
par exemple
var tokenSource =newCancellationTokenSource();var token = tokenSource.Token;Task.Factory.StartNew(()=>DoSomeWork(1, token), token);staticvoidDoSomeWork(int taskNum,CancellationToken ct){// Do work here, checking and acting on ct.IsCancellationRequested where applicable, }
J'utilise une approche mixte pour annuler une tâche.
Tout d'abord, j'essaie de l'annuler poliment en utilisant l' annulation .
S'il est toujours en cours d'exécution (par exemple à cause d'une erreur d'un développeur), alors ne vous comportez pas correctement et tuez-le en utilisant une méthode Abort à l' ancienne .
Consultez un exemple ci-dessous:
privateCancellationTokenSource taskToken;privateAutoResetEvent awaitReplyOnRequestEvent =newAutoResetEvent(false);voidMain(){// Start a task which is doing nothing but sleeps 1sLaunchTaskAsync();Thread.Sleep(100);// Stop the taskStopTask();}/// <summary>/// Launch task in a new thread/// </summary>voidLaunchTaskAsync(){
taskToken =newCancellationTokenSource();Task.Factory.StartNew(()=>{try{//Capture the thread
runningTaskThread =Thread.CurrentThread;// Run the taskif(taskToken.IsCancellationRequested||!awaitReplyOnRequestEvent.WaitOne(10000))return;Console.WriteLine("Task finished!");}catch(Exception exc){// Handle exception}}, taskToken.Token);}/// <summary>/// Stop running task/// </summary>voidStopTask(){// Attempt to cancel the task politelyif(taskToken !=null){if(taskToken.IsCancellationRequested)return;else
taskToken.Cancel();}// Notify a waiting thread that an event has occurredif(awaitReplyOnRequestEvent !=null)
awaitReplyOnRequestEvent.Set();// If 1 sec later the task is still running, kill it cruellyif(runningTaskThread !=null){try{
runningTaskThread.Join(TimeSpan.FromSeconds(1));}catch(Exception ex){
runningTaskThread.Abort();}}}
Les tâches ont un support de première classe pour l'annulation via des jetons d'annulation . Créez vos tâches avec des jetons d'annulation et annulez les tâches via ceux-ci explicitement.
Vous pouvez utiliser a CancellationTokenpour contrôler si la tâche est annulée. Parlez-vous de l'abandonner avant qu'il ne démarre ("tant pis, je l'ai déjà fait"), ou de l'interrompre au milieu? Si le premier, le CancellationTokenpeut être utile; dans ce dernier cas, vous devrez probablement implémenter votre propre mécanisme de "renflouement" et vérifier aux points appropriés dans l'exécution de la tâche si vous devez échouer rapidement (vous pouvez toujours utiliser le CancellationToken pour vous aider, mais c'est un peu plus manuel).
Les tâches sont en cours d'exécution sur le ThreadPool (au moins, si vous utilisez la fabrique par défaut), donc l'abandon du thread ne peut pas affecter les tâches. Pour abandonner des tâches, consultez Annulation de tâche sur msdn.
J'ai essayé CancellationTokenSourcemais je ne peux pas faire ça. Et je l'ai fait à ma manière. Et il fonctionne.
namespace Blokick.Provider{publicclassSignalRConnectProvider{publicSignalRConnectProvider(){}publicboolIsStopRequested{get;set;}=false;//1-)This is important and default `false`.publicasyncTask<string>ConnectTab(){string messageText ="";for(int count =1; count <20; count++){if(count ==1){//Do stuff.}try{//Do stuff.}catch(Exception ex){//Do stuff.}if(IsStopRequested)//3-)This is important. The control of the task stopping request. Must be true and in inside.{return messageText ="Task stopped.";//4-) And so return and exit the code and task.}if(Connected){//Do stuff.}if(count ==19){//Do stuff.}}return messageText;}}}
Et une autre classe de l'appel de la méthode:
namespace Blokick.Views{[XamlCompilation(XamlCompilationOptions.Compile)]publicpartialclassMessagePerson:ContentPage{SignalRConnectProvider signalR =newSignalRConnectProvider();publicMessagePerson(){InitializeComponent();
signalR.IsStopRequested=true;// 2-) And this. Make true if running the task and go inside if statement of the IsStopRequested property.if(signalR.ChatHubProxy!=null){
signalR.Disconnect();}LoadSignalRMessage();}}}
Vous pouvez abandonner une tâche comme un thread si vous pouvez provoquer la création de la tâche sur son propre thread et appeler Abortson Threadobjet. Par défaut, une tâche s'exécute sur un thread de pool de threads ou sur le thread appelant, que vous ne souhaitez généralement pas abandonner.
Pour vous assurer que la tâche obtient son propre thread, créez un planificateur personnalisé dérivé de TaskScheduler. Dans votre implémentation de QueueTask, créez un nouveau thread et utilisez-le pour exécuter la tâche. Plus tard, vous pouvez abandonner le thread, ce qui entraînera l'exécution de la tâche dans un état défectueux avec un ThreadAbortException.
La Thread.Abortméthode doit être utilisée avec prudence. En particulier, lorsque vous l'appelez pour abandonner un thread autre que le thread actuel, vous ne savez pas quel code a été exécuté ou n'a pas pu s'exécuter lorsque ThreadAbortException est levée, ni ne pouvez être certain de l'état de votre application ou de tout état d'application et utilisateur qu'il est chargé de préserver. Par exemple, l'appel Thread.Abortpeut empêcher les constructeurs statiques de s'exécuter ou empêcher la libération de ressources non managées.
Ce code échoue avec une exception d'exécution: System.InvalidOperationException: RunSynchronously ne peut pas être appelé sur une tâche qui a déjà été démarrée.
Theodor Zoulias
1
@TheodorZoulias Bonne prise. Merci. J'ai corrigé le code et généralement amélioré la réponse.
Edward Brey
1
Oui, cela a corrigé le bogue. Une autre mise en garde qu'il convient probablement de mentionner est qu'il Thread.Abortn'est pas pris en charge sur .NET Core. Tenter de l'utiliser là entraîne une exception: System.PlatformNotSupportedException: l'abandon de thread n'est pas pris en charge sur cette plate-forme. Une troisième mise en garde est que le SingleThreadTaskSchedulerne peut pas être utilisé efficacement avec des tâches de style promesse, c'est-à-dire avec des tâches créées avec des asyncdélégués. Par exemple, un incorporé ne await Task.Delay(1000)s'exécute dans aucun thread, il n'est donc pas affecté par les événements de thread.
Réponses:
Vous ne pouvez pas. Les tâches utilisent les threads d'arrière-plan du pool de threads. L'annulation des threads à l'aide de la méthode Abort n'est pas recommandée. Vous pouvez consulter le billet de blog suivant qui explique une manière appropriée d'annuler des tâches à l'aide de jetons d'annulation. Voici un exemple:
la source
Task.Wait(TimeSpan / int)
pour lui donner un délai (basé sur le temps) de l'extérieur.Task
? Quelque chose comme:public int StartNewTask(Action method)
. A l' intérieur duStartNewTask
méthode que je crée un nouveauTask
par:Task task = new Task(() => method()); task.Start();
. Alors, comment puis-je gérer leCancellationToken
? Je voudrais également savoir siThread
je dois implémenter une logique pour vérifier s'il y a des tâches qui sont toujours en suspens et donc les tuer quandForm.Closing
. AvecThreads
j'utiliseThread.Abort()
.L'annulation d'une tâche est facilement possible si vous capturez le thread dans lequel la tâche s'exécute. Voici un exemple de code pour illustrer ceci:
J'ai utilisé Task.Run () pour montrer le cas d'utilisation le plus courant pour cela - en utilisant le confort de Tasks avec l'ancien code à un seul thread, qui n'utilise pas la classe CancellationTokenSource pour déterminer s'il doit être annulé ou non.
la source
CancellationToken
support ...CancellationToken
solution, voire plus simple, sans condition de concurrence, devrait être envisagée. Le code ci-dessus illustre uniquement la méthode, pas le domaine d'utilisation.thread
variable locale). Dans votre code, vous pourriez alors finir par abandonner le thread principal, ce qui n'est pas ce que vous voulez vraiment. Peut-être qu'un contrôle si les threads sont les mêmes avant l'abandon serait une bonne idée, si vous insistez pour abandonnerComme le suggère cet article , cela peut être fait de la manière suivante:
Bien que cela fonctionne, il n'est pas recommandé d'utiliser une telle approche. Si vous pouvez contrôler le code qui s'exécute dans la tâche, vous feriez mieux de gérer correctement l'annulation.
la source
Ce genre de chose est l'une des raisons logistiques pour lesquelles
Abort
est obsolète. D'abord et avant tout, ne pas utiliserThread.Abort()
pour annuler ou arrêter un thread si cela est possible.Abort()
ne doit être utilisé que pour tuer de force un thread qui ne répond pas à des demandes plus pacifiques d'arrêter en temps opportun.Cela étant dit, vous devez fournir un indicateur d'annulation partagé qu'un thread définit et attend pendant que l'autre thread vérifie périodiquement et se termine normalement. .NET 4 comprend une structure spécialement conçue à cet effet, le
CancellationToken
.la source
Vous ne devriez pas essayer de faire cela directement. Concevez vos tâches pour qu'elles fonctionnent avec un CancellationToken et annulez-les de cette façon.
De plus, je recommanderais de changer votre thread principal pour qu'il fonctionne également via un CancellationToken. L'appel
Thread.Abort()
est une mauvaise idée - cela peut entraîner divers problèmes qui sont très difficiles à diagnostiquer. Au lieu de cela, ce thread peut utiliser la même annulation que vos tâches - et la même choseCancellationTokenSource
peut être utilisée pour déclencher l'annulation de toutes vos tâches et de votre thread principal.Cela conduira à une conception beaucoup plus simple et plus sûre.
la source
Pour répondre à la question de Prerak K sur la façon d'utiliser CancellationTokens lorsque vous n'utilisez pas une méthode anonyme dans Task.Factory.StartNew (), vous passez le CancellationToken en tant que paramètre dans la méthode que vous démarrez avec StartNew (), comme indiqué dans l'exemple MSDN ici .
par exemple
la source
J'utilise une approche mixte pour annuler une tâche.
Consultez un exemple ci-dessous:
la source
Les tâches ont un support de première classe pour l'annulation via des jetons d'annulation . Créez vos tâches avec des jetons d'annulation et annulez les tâches via ceux-ci explicitement.
la source
Vous pouvez utiliser a
CancellationToken
pour contrôler si la tâche est annulée. Parlez-vous de l'abandonner avant qu'il ne démarre ("tant pis, je l'ai déjà fait"), ou de l'interrompre au milieu? Si le premier, leCancellationToken
peut être utile; dans ce dernier cas, vous devrez probablement implémenter votre propre mécanisme de "renflouement" et vérifier aux points appropriés dans l'exécution de la tâche si vous devez échouer rapidement (vous pouvez toujours utiliser le CancellationToken pour vous aider, mais c'est un peu plus manuel).MSDN a un article sur l'annulation des tâches: http://msdn.microsoft.com/en-us/library/dd997396.aspx
la source
Les tâches sont en cours d'exécution sur le ThreadPool (au moins, si vous utilisez la fabrique par défaut), donc l'abandon du thread ne peut pas affecter les tâches. Pour abandonner des tâches, consultez Annulation de tâche sur msdn.
la source
J'ai essayé
CancellationTokenSource
mais je ne peux pas faire ça. Et je l'ai fait à ma manière. Et il fonctionne.Et une autre classe de l'appel de la méthode:
la source
Vous pouvez abandonner une tâche comme un thread si vous pouvez provoquer la création de la tâche sur son propre thread et appeler
Abort
sonThread
objet. Par défaut, une tâche s'exécute sur un thread de pool de threads ou sur le thread appelant, que vous ne souhaitez généralement pas abandonner.Pour vous assurer que la tâche obtient son propre thread, créez un planificateur personnalisé dérivé de
TaskScheduler
. Dans votre implémentation deQueueTask
, créez un nouveau thread et utilisez-le pour exécuter la tâche. Plus tard, vous pouvez abandonner le thread, ce qui entraînera l'exécution de la tâche dans un état défectueux avec unThreadAbortException
.Utilisez ce planificateur de tâches:
Commencez votre tâche comme ceci:
Plus tard, vous pouvez abandonner avec:
Notez que la mise en garde concernant l'abandon d'un thread s'applique toujours:
la source
Thread.Abort
n'est pas pris en charge sur .NET Core. Tenter de l'utiliser là entraîne une exception: System.PlatformNotSupportedException: l'abandon de thread n'est pas pris en charge sur cette plate-forme. Une troisième mise en garde est que leSingleThreadTaskScheduler
ne peut pas être utilisé efficacement avec des tâches de style promesse, c'est-à-dire avec des tâches créées avec desasync
délégués. Par exemple, un incorporé neawait Task.Delay(1000)
s'exécute dans aucun thread, il n'est donc pas affecté par les événements de thread.