2012-04-13 9 views
104

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

答えて

161

Cancellation上に読む(.NET 4.0の中に導入して以来、大幅に変更されていないされた)とTask-Based Asynchronous Patternasync方法でCancellationTokenを使用する方法のガイドラインを提供します。

要約すると、取り消しをサポートする各メソッドに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; 
} 
+2

ワウの素晴らしい情報!それが完全に機能したので、非同期メソッドで例外を処理する方法を理解する必要があります。ありがとう!私はあなたが提案したものを読むでしょう。 – Carlo

+1

取り扱いが容易でした。もう一度ありがとう! =) – Carlo

+0

遅い方法にアクセスできない場合、やあ、それを行う方法はありますか?たとえば、slowFuncがブラックボックスにあり、そのメソッドを呼び出すだけのアクセス権があるが、その中の何も変更しないとします。 – Carlo

5

すでに受け入れられている回答に追加したいだけです。私はこれについていましたが、完全なイベントを扱う際には別のルートを使用していました。待っているのではなく、完了したハンドラをタスクに追加します。イベントハンドラは、タスクがキャンセルされた場合、すべての処理はすでにそれだけでイベントハンドラをトリガし、あなたがどうかを確認することができ、あなたのために行われ、このルートでは、この

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

のように見えます

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

それはそこでキャンセルされました。

20

または、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()); 
+0

優れた答え、ありがとうございます。 – Elton

+0

これは非常にトリッキーなようです... async-aswaitの実装全体として。私はそのような構造がソースコードを読みやすくするとは思わない。 – Maxim

関連する問題