176

これらの 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()
{
}
4

4 に答える 4

256

キャンセル(.NET 4.0で導入され、それ以降ほとんど変更されていません)と、メソッドの使用方法に関するガイドラインを提供するタスクベースの非同期パターンについて読んでください。CancellationTokenasync

要約する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;
}
于 2012-04-13T02:43:33.940 に答える
45

または、変更を避けるために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());
于 2015-10-09T13:49:23.660 に答える
6

すでに受け入れられている答えに追加したいだけです。私はこれに固執していましたが、完全なイベントを処理するために別のルートを進んでいました。待機を実行するのではなく、完了したハンドラーをタスクに追加します。

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

このルートでは、すべての処理がすでに完了しています。タスクがキャンセルされると、イベントハンドラーがトリガーされるだけで、そこでキャンセルされたかどうかを確認できます。

于 2012-08-02T01:22:15.020 に答える