2012-04-13 18 views
104

Gram w te zadania Windows 8 WinRT i próbuję anulować zadanie za pomocą poniższej metody i działa do pewnego momentu. Zostanie wywołana metoda CancelNotification, co powoduje, że uważasz, że zadanie zostało anulowane, ale w tle zadanie nadal działa, a po jego zakończeniu status zadania jest zawsze zakończony i nigdy nie jest anulowany. Czy istnieje sposób na całkowite zatrzymanie zadania po jego anulowaniu?Jak anulować zadanie w oczekiwaniu?

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

Odpowiedz

161

Przeczytaj na Cancellation (który został wprowadzony w .NET 4.0 i jest w dużej mierze niezmieniona od tamtego czasu) i Task-Based Asynchronous Pattern, który zawiera wytyczne dotyczące sposobu korzystania CancellationToken z async metod.

Podsumowując, należy przekazać CancellationToken do każdej metody, która obsługuje anulowanie, a ta metoda musi okresowo ją sprawdzać.

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

Wow świetne informacje! To działało idealnie, teraz muszę wymyślić jak sobie poradzić z wyjątkiem w asynchronicznej metodzie. Dzięki! Przeczytam rzeczy, które zasugerowałeś. – Carlo

+1

Obsługa ok była łatwa. Znowu dziękuję bardzo !! =) – Carlo

+0

Hej człowieku, czy jest sposób, aby to zrobić, jeśli nie mam dostępu do powolnej metody? Załóżmy na przykład, że slowFunc był w blackboksie i masz dostęp tylko do wywołania metody, ale nie do modyfikowania czegokolwiek w niej zawartego? – Carlo

5

Po prostu chcę dodać do już zaakceptowanej odpowiedzi. Utknąłem na tym, ale szedłem inną drogą na obsługę całego wydarzenia. Zamiast uruchamiać się, dodam do zadania ukończony moduł obsługi.

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

Jeżeli obsługi zdarzeń wygląda następująco

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

Z tej trasie, cała obsługa jest już wykonane, gdy zadanie jest anulowane po prostu wywołuje obsługi zdarzeń i można sprawdzić, czy zostało tam anulowane.

20

Lub, w celu uniknięcia modyfikowania slowFunc (mówią, że nie mają dostępu do kodu źródłowego na przykład):

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 

Można również użyć ładne metody rozszerzenie z https://github.com/StephenCleary/AsyncEx i mieć to wygląda proste:

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

Znakomita odpowiedź, dziękuję. – Elton

+0

Wygląda bardzo podchwytliwie ... na całe wdrożenie. Nie sądzę, aby takie konstrukcje czyniły kod źródłowy bardziej czytelnym. – Maxim