c# - example - ¿Cómo cancelo/cancelo TPL tareas?
cancel task run c# (11)
En un hilo, creo algún System.Threading.Task
y comienzo cada tarea.
Cuando hago un .Abort()
para matar el hilo, las tareas no se cancelan.
¿Cómo puedo transmitir el .Abort()
a mis tareas?
Al igual que sugiere esta publicación , esto se puede hacer de la siguiente manera:
int Foo(CancellationToken token)
{
Thread t = Thread.CurrentThread;
using (token.Register(t.Abort))
{
// compute-bound work here
}
}
Aunque funciona, no se recomienda utilizar dicho enfoque. Si puede controlar el código que se ejecuta en la tarea, será mejor que proceda con el manejo adecuado de la cancelación.
Anular una tarea es fácilmente posible si captura el hilo en el que se está ejecutando la tarea. Aquí hay un código de ejemplo para demostrar esto:
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();
}
Usé Task.Run () para mostrar el caso de uso más común para esto, utilizando la comodidad de las tareas con el código de un solo subproceso antiguo, que no utiliza la clase CancellationTokenSource para determinar si se debe cancelar o no.
Este tipo de cosas es una de las razones logísticas por las cuales Abort
está en desuso. En primer lugar, no use Thread.Abort()
para cancelar o detener un hilo si es posible. Abort()
solo debe usarse para matar a la fuerza un hilo que no responde a solicitudes más pacíficas de detenerse de manera oportuna.
Dicho esto, debe proporcionar un indicador de cancelación compartido que establezca un hilo y espere mientras el otro hilo lo revisa periódicamente y sale con gracia. .NET 4 incluye una estructura diseñada específicamente para este propósito, el CancellationToken
.
Intenté CancellationTokenSource
pero no puedo hacer esto. Y lo hice a mi manera. Y funciona.
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;
}
}
}
Y otra clase de llamar al método:
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();
}
}
}
La tarea se está ejecutando en el ThreadPool (al menos, si está utilizando la fábrica predeterminada), por lo que cancelar el hilo no puede afectar las tareas. Para abortar tareas, vea Cancellation en msdn.
Las tareas tienen soporte de primera clase para la cancelación mediante Cancellation . Cree sus tareas con tokens de cancelación y cancele las tareas a través de estos explícitamente.
No deberías intentar hacer esto directamente. Diseñe sus tareas para que funcionen con un CancellationToken y cancélelas de esta manera.
Además, recomendaría cambiar su hilo principal para que funcione a través de un CancellationToken también. Calling Thread.Abort()
es una mala idea: puede dar lugar a varios problemas que son muy difíciles de diagnosticar. En cambio, ese hilo puede usar la misma Cancellation que usan sus tareas, y el mismo CancellationTokenSource
se puede utilizar para activar la cancelación de todas sus tareas y su hilo principal.
Esto conducirá a un diseño mucho más simple y seguro.
No puedes. Las tareas usan subprocesos de fondo del grupo de subprocesos. Tampoco se recomienda cancelar subprocesos utilizando el método Abortar. Puede echar un vistazo a la siguiente publicación de blog que explica una forma adecuada de cancelar tareas utilizando tokens de cancelación. Aquí hay un ejemplo:
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();
}
}
Para responder la pregunta de Prerak K sobre cómo utilizar CancellationTokens cuando no utiliza un método anónimo en Task.Factory.StartNew (), pasa el CancellationToken como parámetro al método que está comenzando con StartNew (), como se muestra en el ejemplo de MSDN here .
p.ej
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,
}
Puede utilizar un CancellationToken
para controlar si la tarea se cancela. ¿Estás hablando de abortar antes de que se inicie ("no importa, ya lo hice"), o interrumpirlo en el medio? Si el primero, el CancellationToken
puede ser útil; en este último caso, es probable que deba implementar su propio mecanismo de "rescate" y verificar en los puntos apropiados en la ejecución de la tarea si debe fallar rápidamente (aún puede utilizar el CancelToken para ayudarlo, pero es un poco más manual).
MSDN tiene un artículo sobre la cancelación de tareas: Cancellation
Utilizo un enfoque mixto para cancelar una tarea.
- En primer lugar, estoy tratando de cancelarlo educadamente con el uso de la Cancellation .
- Si aún se está ejecutando (por ejemplo, debido a un error del desarrollador), entonces se portará mal y lo matará usando un método de Abort vieja escuela.
Verifique un ejemplo a continuación:
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();
}
}
}