2012-04-13 118 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 Pattern,它提供了有關如何使用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; 
} 
+2

哇偉大的信息!這工作完美,現在我需要弄清楚如何在異步方法中處理異常。謝啦!我會讀你建議的東西。 – Carlo 2012-04-13 02:48:55

+1

好處理很簡單。再次感謝! =) – Carlo 2012-04-13 02:52:00

+0

嘿,如果我沒有訪問慢方法,有沒有辦法做到這一點?例如,假設slowFunc位於黑盒子中,並且您只能訪問該方法,但不能修改其中的任何內容? – Carlo 2012-04-13 19:12:55

5

我只是想添加到已經接受的答案。我被困在這件事上,但我正在處理完整事件的另一條路線。我不是在等待,而是爲任務添加完成的處理程序。

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

有了這條路,所有的處理都已經完成了你,當任務被取消,它只是觸發事件處理程序,你可以看到,如果它在那裏被取消了。

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 2017-07-20 16:18:37

+0

它看起來非常棘手...作爲整個異步等待實現。我不認爲這樣的結構會使源代碼更具可讀性。 – Maxim 2017-10-30 17:58:24