Как отменить задание в ожидании?


Я играю с этими задачами Windows 8 WinRT, и я пытаюсь отменить задачу, используя метод ниже, и он работает до некоторой степени. Метод CancelNotification действительно вызывается, что заставляет вас думать, что задача была отменена, но в фоновом режиме задача продолжает работать, а затем после ее завершения состояние задачи всегда завершено и никогда не отменяется. Есть ли способ полностью остановить задачу, когда она отменена?

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()
{
}
3 125

3 ответа:

Читать далее отмена (который был представлен в .NET 4.0 и в значительной степени не изменился с тех пор) и Асинхронный Шаблон На Основе Задач, который предоставляет рекомендации о том, как использовать CancellationToken С async методы.

подводя итог, вы передаете CancellationToken в каждом методе, который поддерживает отмену, и этот метод должен периодически проверять его.

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;
}

или, чтобы избежать изменения slowFunc (допустим у вас нет доступа к исходному коду например):

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

вы также можете использовать хорошие методы расширения от https://github.com/StephenCleary/AsyncEx и это выглядит так просто, как:

await Task.WhenAny(task, source.Token.AsTask());

Я просто хочу добавить к уже принято отвечать. Я застрял на этом, но я шел другим путем по обработке полного события. Вместо того, чтобы запускать await, я добавляю завершенный обработчик к задаче.

Comments.AsAsyncAction().Completed += new AsyncActionCompletedHandler(CommentLoadComplete);

где обработчик событий выглядит так

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;
}

С этим маршрутом вся обработка уже выполнена для вас, когда задача отменена, она просто запускает обработчик событий, и вы можете увидеть, была ли она отменена там.