Dans un thread, je crée un certain System.Threading.Task
et lance chaque tâche.
Lorsque je fais un .Abort()
pour tuer le thread, les tâches ne sont pas annulées.
Comment puis-je transmettre le .Abort()
à mes tâches?
Vous ne pouvez pas Les tâches utilisent des 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 l’article suivant qui explique comment annuler les tâches à l’aide de jetons d’annulation. Voici un exemple:
class Program { static void Main() { var ts = new CancellationTokenSource(); CancellationToken ct = ts.Token; Task.Factory.StartNew(() => { while (true) { // do some heavy work here Thread.Sleep(100); if (ct.IsCancellationRequested) { // another thread decided to cancel Console.WriteLine("task canceled"); break; } } }, ct); // Simulate waiting 3s for the task to complete Thread.Sleep(3000); // Can't wait anymore => cancel this task ts.Cancel(); Console.ReadLine(); } }
Abandonner une tâche est facilement possible si vous capturez le thread dans lequel la tâche est exécutée. Voici un exemple de code pour démontrer ceci:
void Main() { 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 displayed Console.WriteLine("Task finished!"); }); //This is needed in the example to avoid thread being still NULL Thread.Sleep(10); //Cancel the task by aborting the thread thread.Abort(); }
J’ai utilisé Task.Run () pour afficher le cas d’utilisation le plus courant – en utilisant le confort des tâches avec l’ancien code mono-thread, qui n’utilise pas la classe CancellationTokenSource pour déterminer s’il doit être annulé ou non.
Comme ce post le suggère, cela peut se faire de la manière suivante:
int Foo(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.
Ce genre de chose est l’une des raisons logistiques pour lesquelles Abort
est déconseillé. Avant tout, n’utilisez pas Thread.Abort()
pour annuler ou arrêter un thread si possible. Abort()
ne devrait être utilisé que pour tuer un thread qui ne répond pas à des requêtes plus pacifiques pour s’arrêter rapidement.
Cela étant dit, vous devez fournir un indicateur d’annulation partagé indiquant qu’un thread se positionne et attend pendant que l’autre thread vérifie périodiquement et quitte normalement. .NET 4 comprend une structure spécialement conçue à cet effet, le CancellationToken
.
Vous ne devriez pas essayer de le faire directement. Concevez vos tâches pour travailler avec un CancellationToken et annulez-les de cette façon.
En outre, je recommanderais également de changer votre thread principal pour qu’il fonctionne via un CancellationToken. Appeler Thread.Abort()
est une mauvaise idée – cela peut entraîner divers problèmes très difficiles à diagnostiquer. Au lieu de cela, ce thread peut utiliser la même Annulation que vos tâches – et le même CancellationTokenSource
peut être utilisé 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 l’utilisation de CancellationTokens lorsque vous n’utilisez pas de méthode anonyme dans Task.Factory.StartNew (), vous devez transmettre le paramètre CancellationToken en tant que paramètre à la méthode que vous avez démarrée avec StartNew (), comme illustré dans l’exemple MSDN ici
par exemple
var tokenSource = new CancellationTokenSource(); var token = tokenSource.Token; Task.Factory.StartNew( () => DoSomeWork(1, token), token); static void DoSomeWork(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.
Consultez un exemple ci-dessous:
private CancellationTokenSource taskToken; private AutoResetEvent awaitReplyOnRequestEvent = new AutoResetEvent(false); void Main() { // Start a task which is doing nothing but sleeps 1s LaunchTaskAsync(); Thread.Sleep(100); // Stop the task StopTask(); } /// /// Launch task in a new thread /// void LaunchTaskAsync() { taskToken = new CancellationTokenSource(); Task.Factory.StartNew(() => { try { //Capture the thread runningTaskThread = Thread.CurrentThread; // Run the task if (taskToken.IsCancellationRequested || !awaitReplyOnRequestEvent.WaitOne(10000)) return; Console.WriteLine("Task finished!"); } catch (Exception exc) { // Handle exception } }, taskToken.Token); } /// /// Stop running task /// void StopTask() { // Attempt to cancel the task politely if (taskToken != null) { if (taskToken.IsCancellationRequested) return; else taskToken.Cancel(); } // Notify a waiting thread that an event has occurred if (awaitReplyOnRequestEvent != null) awaitReplyOnRequestEvent.Set(); // If 1 sec later the task is still running, kill it cruelly if (runningTaskThread != null) { try { runningTaskThread.Join(TimeSpan.FromSeconds(1)); } catch (Exception ex) { runningTaskThread.Abort(); } } }
Vous pouvez utiliser un CancellationToken
pour contrôler si la tâche est annulée. Est-ce que vous parlez de l’avorter avant qu’il ne commence (“Mince, j’ai déjà fait ça”), ou de l’interrompre en fait? Si le premier, l’ CancellationToken
peut être utile; si c’est le cas, vous devrez probablement implémenter votre propre mécanisme de «renflouement» et vérifier à des moments 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
Les tâches ont un support de première classe pour l’annulation via les jetons d’annulation . Créez vos tâches avec des jetons d’annulation et annulez les tâches de manière explicite.
La tâche est en cours d’exécution sur le ThreadPool (du moins, si vous utilisez la fabrique par défaut), l’abandon du thread ne peut pas affecter les tâches. Pour annuler des tâches, voir Annulation de tâches sur msdn.
J’ai essayé CancellationTokenSource
mais je ne peux pas le faire. Et je l’ai fait à ma manière. Et il fonctionne.
namespace Blokick.Provider { public class SignalRConnectProvider { public SignalRConnectProvider() { } public bool IsStopRequested { get; set; } = false; //1-)This is important and default `false`. public async Task ConnectTab() { ssortingng 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.Comstack)] public partial class MessagePerson : ContentPage { SignalRConnectProvider signalR = new SignalRConnectProvider(); public MessagePerson() { 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(); } } }