Dans un fil, j'en crée System.Threading.Task
et démarre chaque tâche.
Lorsque je fais un .Abort()
pour tuer le thread, les tâches ne sont pas abandonnées.
Comment puis-je transmettre le .Abort()
à mes tâches?
Dans un fil, j'en crée System.Threading.Task
et démarre chaque tâche.
Lorsque je fais un .Abort()
pour tuer le thread, les tâches ne sont pas abandonnées.
Comment puis-je transmettre le .Abort()
à mes tâches?
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:
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();
}
}
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 du StartNewTask
méthode que je crée un nouveau Task
par: Task task = new Task(() => method()); task.Start();
. Alors, comment puis-je gérer le CancellationToken
? Je voudrais également savoir si Thread
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 quand Form.Closing
. Avec Threads
j'utilise Thread.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:
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 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.
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 abandonner
Comme le suggère cet article , 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 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 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 CancellationTokenSource
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.
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 = 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();
}
/// <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();
}
}
}
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 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, le CancellationToken
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
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é CancellationTokenSource
mais je ne peux pas faire ça. 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<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)]
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();
}
}
}
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
son Thread
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 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
.
Utilisez ce planificateur de tâches:
class SingleThreadTaskScheduler : TaskScheduler
{
public Thread TaskThread { get; private set; }
protected override void QueueTask(Task task)
{
TaskThread = new Thread(() => TryExecuteTask(task));
TaskThread.Start();
}
protected override IEnumerable<Task> GetScheduledTasks() => throw new NotSupportedException(); // Unused
protected override bool NotSupportedException(Task task, bool taskWasPreviouslyQueued) => throw new NotSupportedException(); // Unused
}
Commencez votre tâche comme ceci:
var scheduler = new SingleThreadTaskScheduler();
var task = Task.Factory.StartNew(action, cancellationToken, TaskCreationOptions.LongRunning, scheduler);
Plus tard, vous pouvez abandonner avec:
scheduler.TaskThread.Abort();
Notez que la mise en garde concernant l'abandon d'un thread s'applique toujours:
La
Thread.Abort
mé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'appelThread.Abort
peut empêcher les constructeurs statiques de s'exécuter ou empêcher la libération de ressources non managées.
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 le SingleThreadTaskScheduler
ne peut pas être utilisé efficacement avec des tâches de style promesse, c'est-à-dire avec des tâches créées avec des async
dé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.