c# - ¿Cómo cancelar una tarea en espera?
task-parallel-library .net-4.5 (3)
Lea sobre Cancellation (que se introdujo en .NET 4.0 y no se ha modificado en gran medida desde entonces) y el Patrón asincrónico basado en tareas , que proporciona directrices sobre cómo utilizar CancellationToken
con métodos async
.
Para resumir, pasa un CancellationToken
a cada método que admite la cancelación, y ese método debe verificarlo periódicamente.
private async Task TryTask()
{
CancellationTokenSource source = new CancellationTokenSource();
source.CancelAfter(TimeSpan.FromSeconds(1));
Task<int> task = Task.Run(() => slowFunc(1, 2, source.Token), source.Token);
// (A canceled task will raise an exception when awaited).
await task;
}
private int slowFunc(int a, int b, CancellationToken cancellationToken)
{
string someString = string.Empty;
for (int i = 0; i < 200000; i++)
{
someString += "a";
if (i % 1000 == 0)
cancellationToken.ThrowIfCancellationRequested();
}
return a + b;
}
Estoy jugando con estas tareas de Windows 8 WinRT, y estoy tratando de cancelar una tarea usando el siguiente método, y funciona hasta cierto punto. Se llama al método CancelNotification, lo que le hace pensar que la tarea se canceló, pero en el fondo la tarea sigue ejecutándose, y luego de que se completa, el estado de la tarea siempre se completa y nunca se cancela. ¿Hay alguna forma de detener completamente la tarea cuando se cancela?
private async void TryTask()
{
CancellationTokenSource source = new CancellationTokenSource();
source.Token.Register(CancelNotification);
source.CancelAfter(TimeSpan.FromSeconds(1));
var task = Task<int>.Factory.StartNew(() => slowFunc(1, 2), source.Token);
await task;
if (task.IsCompleted)
{
MessageDialog md = new MessageDialog(task.Result.ToString());
await md.ShowAsync();
}
else
{
MessageDialog md = new MessageDialog("Uncompleted");
await md.ShowAsync();
}
}
private int slowFunc(int a, int b)
{
string someString = string.Empty;
for (int i = 0; i < 200000; i++)
{
someString += "a";
}
return a + b;
}
private void CancelNotification()
{
}
O bien, para evitar modificar slowFunc
(supongamos que no tiene acceso al código fuente, por ejemplo):
var source = new CancellationTokenSource(); //original code
source.Token.Register(CancelNotification); //original code
source.CancelAfter(TimeSpan.FromSeconds(1)); //original code
var completionSource = new TaskCompletionSource<object>(); //New code
source.Token.Register(() => completionSource.TrySetCanceled()); //New code
var task = Task<int>.Factory.StartNew(() => slowFunc(1, 2), source.Token); //original code
//original code: await task;
await Task.WhenAny(task, completionSource.Task); //New code
También puede usar buenos métodos de extensión desde https://github.com/StephenCleary/AsyncEx y tener un aspecto tan simple como:
await Task.WhenAny(task, source.Token.AsTask());
Solo quiero agregar a la respuesta ya aceptada. Estaba atascado en esto, pero iba a tomar una ruta diferente para manejar el evento completo. En lugar de esperar, agrego un controlador completo a la tarea.
Comments.AsAsyncAction().Completed += new AsyncActionCompletedHandler(CommentLoadComplete);
Donde el controlador de eventos se ve así
private void CommentLoadComplete(IAsyncAction sender, AsyncStatus status )
{
if (status == AsyncStatus.Canceled)
{
return;
}
CommentsItemsControl.ItemsSource = Comments.Result;
CommentScrollViewer.ScrollToVerticalOffset(0);
CommentScrollViewer.Visibility = Visibility.Visible;
CommentProgressRing.Visibility = Visibility.Collapsed;
}
Con esta ruta, todo el manejo ya está hecho para ti, cuando la tarea se cancela, solo activa el controlador de eventos y puedes ver si se canceló allí.