En un hilo, creo algunos 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?
En un hilo, creo algunos 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?
Respuestas:
No puedes Las tareas usan subprocesos de fondo del grupo de subprocesos. Tampoco se recomienda cancelar hilos usando 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();
}
}
Task.Wait(TimeSpan / int)
para darle una fecha límite (basada en el tiempo) desde el exterior.
Task
? Algo así como: public int StartNewTask(Action method)
. Dentro del StartNewTask
método se crea una nueva Task
por: Task task = new Task(() => method()); task.Start();
. Entonces, ¿cómo puedo gestionar el CancellationToken
? También me gustaría saber si Thread
necesito implementar una lógica para verificar si hay algunas Tareas que aún están pendientes y así matarlas cuando Form.Closing
. Con Threads
lo uso Thread.Abort()
.
Anular una tarea es fácilmente posible si captura el hilo en el que se ejecuta 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: usar la comodidad de Tareas con código antiguo de subproceso único, que no usa la clase CancellationTokenSource para determinar si debe cancelarse o no.
CancellationToken
soporte ...
CancellationToken
deben considerar soluciones simples o incluso más simples que estén libres de condiciones de carrera. El código anterior solo ilustra el método, no el área de uso.
thread
variable local). En su código, puede terminar abortando el hilo principal, que no es lo que realmente quiere. Quizás sea una buena
Como 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 este enfoque. Si puede controlar el código que se ejecuta en la tarea, será mejor que se encargue de la cancelación adecuada.
Este tipo de cosas es una de las razones logísticas por las que 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 para detenerse de manera oportuna.
Dicho esto, debe proporcionar un indicador de cancelación compartido de que un subproceso se establece y espera mientras el otro subproceso verifica periódicamente y sale correctamente. .NET 4 incluye una estructura diseñada específicamente para este propósito, el CancellationToken
.
No debe intentar hacer esto directamente. Diseñe sus tareas para trabajar 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. Llamar Thread.Abort()
es una mala idea: puede provocar varios problemas que son muy difíciles de diagnosticar. En cambio, ese hilo puede usar la misma Cancelación que usan sus tareas, y lo mismo CancellationTokenSource
puede usarse para desencadenar la cancelación de todas sus tareas y su hilo principal.
Esto conducirá a un diseño mucho más simple y seguro.
Para responder la pregunta de Prerak K sobre cómo usar CancellationTokens cuando no se usa un método anónimo en Task.Factory.StartNew (), se pasa el CancellationToken como parámetro al método que está comenzando con StartNew (), como se muestra en el ejemplo de MSDN aquí .
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,
}
Utilizo un enfoque mixto para cancelar una tarea.
Vea 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();
}
}
}
Las tareas tienen soporte de primera clase para la cancelación a través de tokens de cancelación . Cree sus tareas con tokens de cancelación y cancele las tareas a través de estos explícitamente.
Puede usar a CancellationToken
para controlar si la tarea se cancela. ¿Estás hablando de abortarlo antes de que comience ("no importa, ya hice esto") o de interrumpirlo en el medio? Si es lo primero, CancellationToken
puede ser útil; si es lo último, probablemente necesitará 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 usar CancellationToken para ayudarlo, pero es un poco más manual).
MSDN tiene un artículo sobre la cancelación de tareas: http://msdn.microsoft.com/en-us/library/dd997396.aspx
Las tareas se ejecutan en ThreadPool (al menos, si está utilizando la fábrica predeterminada), por lo que abortar el hilo no puede afectar las tareas. Para abortar tareas, vea Cancelación de tareas en msdn.
Lo 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();
}
}
}
Puede abortar una tarea como un hilo si puede hacer que la tarea se cree en su propio hilo y llamar Abort
a su Thread
objeto. De forma predeterminada, una tarea se ejecuta en un subproceso de grupo de subprocesos o en el subproceso de llamada, ninguno de los cuales normalmente desea anular.
Para garantizar que la tarea tenga su propio hilo, cree un planificador personalizado derivado de TaskScheduler
. En su implementación de QueueTask
, cree un nuevo hilo y úselo para ejecutar la tarea. Más tarde, puede abortar el subproceso, lo que hará que la tarea se complete en un estado defectuoso con un ThreadAbortException
.
Use este programador de tareas:
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
}
Comience su tarea así:
var scheduler = new SingleThreadTaskScheduler();
var task = Task.Factory.StartNew(action, cancellationToken, TaskCreationOptions.LongRunning, scheduler);
Más tarde, puedes abortar con:
scheduler.TaskThread.Abort();
Tenga en cuenta que la advertencia sobre abortar un hilo todavía se aplica:
El
Thread.Abort
método debe usarse con precaución. Particularmente cuando lo llama para abortar un hilo que no sea el hilo actual, no sabe qué código se ha ejecutado o no se pudo ejecutar cuando se lanza ThreadAbortException , ni puede estar seguro del estado de su aplicación o de cualquier aplicación y estado de usuario que es responsable de preservar. Por ejemplo, llamarThread.Abort
puede evitar que los constructores estáticos se ejecuten o evitar la liberación de recursos no administrados.
Thread.Abort
no es compatible con .NET Core. Intentar usarlo da como resultado una excepción: System.PlatformNotSupportedException: Thread abort no es compatible con esta plataforma. Una tercera advertencia es que SingleThreadTaskScheduler
no se puede usar de manera efectiva con tareas de estilo prometedor, en otras palabras, con tareas creadas con async
delegados. Por ejemplo, un incrustado se await Task.Delay(1000)
ejecuta en ningún subproceso, por lo que no se ve afectado por los eventos de subproceso.