Dans un fil de discussion, je crée un System.Threading.Task
et lance chaque tâche.
Lorsque je fais .Abort()
pour tuer le thread, les tâches ne sont pas abandonnées.
Comment puis-je transmettre la .Abort()
à mes tâches?
Tu ne peux pas. Les tâches utilisent des threads d'arrière-plan du pool de threads. Annuler également les threads à l'aide de la méthode Abort n'est pas recommandé. Vous pouvez jeter un oeil au blog suivant qui explique comment annuler correctement des 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 fil dans lequel la tâche est en cours d'exécution. Voici un exemple de code pour illustrer 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 montrer le cas d'utilisation le plus courant: utiliser le confort des tâches avec l'ancien code à thread unique, qui n'utilise pas la classe CancellationTokenSource pour déterminer s'il doit être annulé ou non.
Comme cet article suggère, cela peut être fait 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 une 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é. Tout d’abord, n’utilisez pas Thread.Abort()
pour annuler ou arrêter un thread, dans la mesure du 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 voulu.
Cela dit, vous devez fournir un indicateur d'annulation partagée qu'un thread définit et attend pendant que l'autre thread vérifie et se termine régulièrement. .NET 4 inclut une structure spécialement conçue à cet effet, le CancellationToken
.
Vous ne devriez pas essayer de le faire directement. Concevez vos tâches avec un CancellationToken et annulez-les de cette façon.
En outre, je vous recommanderais de modifier votre thread principal pour qu'il fonctionne également via un CancellationToken. Appeler Thread.Abort()
est une mauvaise idée. Cela peut entraîner divers problèmes très difficiles à diagnostiquer. À la place, ce fil peut utiliser le même Annulation que vos tâches utilisent - et la même CancellationTokenSource
peuvent être utilisés pour déclencher l'annulation de all de vos tâches et de votre fil principal.
Cela conduira à une conception beaucoup plus simple et plus sûre.
Pour répondre à la question de Prerak K sur la manière d'utiliser AnnulationTokens lorsque vous n'utilisez pas de méthode anonyme dans Task.Factory.StartNew (), vous transmettez AnnulationToken en tant que paramètre à la méthode que vous démarrez avec StartNew (), comme indiqué 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.
Commander 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();
}
/// <summary>
/// Launch task in a new thread
/// </summary>
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);
}
/// <summary>
/// Stop running task
/// </summary>
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 une CancellationToken
pour contrôler si la tâche est annulée. Parlez-vous de l'avorter avant de commencer ("tant pis, je l'ai déjà fait"), ou de l'interrompre au milieu? Si c'est le cas, la CancellationToken
peut être utile; Dans ce dernier cas, vous devrez probablement mettre en œuvre votre propre mécanisme de "sauvetage" et vérifier aux étapes appropriées de l'exécution de la tâche si vous devez échouer rapidement (vous pouvez toujours utiliser CancellationToken pour vous aider, mais c'est un peu plus manuel).
MSDN a publié un article sur l'annulation des tâches: http://msdn.Microsoft.com/en-us/library/dd997396.aspx
Les tâches sont en cours d'exécution sur le ThreadPool (au moins si vous utilisez la fabrique par défaut), si bien que l'abandon du thread ne peut pas affecter les tâches. Pour annuler des tâches, voir Annulation de tâche sur msdn.
Les tâches ont un support de première classe pour l'annulation via jetons d'annulation . Créez vos tâches avec des jetons d'annulation et annulez-les explicitement.
J'ai essayé CancellationTokenSource
mais je ne peux pas faire ça. Et j'ai fait cela à 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<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'appelant la méthode:
namespace Blokick.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
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();
}
}
}