2015-05-14 12 views
5

Mam kod, który obniżam ze słów kluczowych .NET 4.5: async i await do .NET 4.0. Używam ContinueWith, aby utworzyć kontynuację podobną do tej, jak działa await.Jak złapać wyjątek OperationCanceledException podczas korzystania z funkcji ContinueWith

Zasadniczo mój stary kod:

var tokenSource = newCancellationTokenSource(); 
var myTask = Task.Run(() => 
{ 
    return MyStaticClass.DoStuff(tokenSource.Token); 
}, tokenSource.Token); 
try 
{ 
    var result = await myTask; 
    DoStuffWith(result); 
} 
catch (OperationCanceledException) 
{ 
    // Cancel gracefully. 
} 

(Jak można się spodziewać, MyStaticClass.DoStuff(token) regularnie wzywa token.ThrowIfCancellationRequested().)

Mój nowy kod wygląda następująco:

var tokenSource = new CancellationTokenSource(); 
try 
{ 
    Task.Factory.StartNew(() => 
    { 
     return MyStaticClass.DoStuff(tokenSource.Token); 
    }, tokenSource.Token) 
    .ContinueWith(task => 
    { 
     var param = new object[1]; 
     param[0] = task.Result; 
     // I need to use Invoke here because "DoStuffWith()" does UI stuff. 
     Invoke(new MyDelegate(DoStuffWith, param)); 
    }); 
} 
catch (OperationCanceledException) 
{ 
    // Cancel gracefully. 
} 

jednak OperationCanceledException nigdy nie zostanie przechwycony. Co się dzieje? Gdzie mogę umieścić blok try/catch?

+5

Zauważ, że możesz użyć czekają z .NET 4.0 używając https://www.nuget.org/packages/Microsoft.Bcl.Async/ –

Odpowiedz

6

Anulowanie jest obsługiwane inaczej niż inne wyjątki. W zasadzie, można użyć tego wzoru:

Task.Factory.StartNew(() => 
{ 
    // The task 
}, tokenSource.Token) 
.ContinueWith(task => 
{ 
    // The normal stuff 
}, TaskContinuationOptions.OnlyOnRanToCompletion) 
.ContinueWith(task => 
{ 
    // Handle cancellation 
}, TaskContinuationOptions.OnlyOnCanceled) 
.ContinueWith(task => 
{ 
    // Handle other exceptions 
}, TaskContinuationOptions.OnlyOnFaulted); 

lub do alternatywnego One:

Task.Factory.StartNew(() => 
{ 
    // The task 
}, tokenSource.Token) 
.ContinueWith(task => 
{ 
    switch (task.Status) 
    { 
    case TaskStatus.RanToCompletion: 
     // The normal stuff 
     break; 
    case TaskStatus.Canceled: 
     // Handle cancellation 
     break; 
    case TaskStatus.Faulted: 
     // Handle other exceptions 
     break; 
    } 
}); 

W twoim przypadku, nie jesteś połowu cokolwiek, ponieważ:

  • Task.Factory.StartNew natychmiast powraca i zawsze się uda.
  • Twój kontynuacja zawsze biegnie
  • Dostęp task.Result zgłasza AggregateException ponieważ zadanie jest anulowane
  • Wyjątkiem nie jest obsługiwane przez cokolwiek ponieważ jest wyrzucane z wątku puli wątków. Ups. Co dzieje się dalej depends on the framework version:

    • W .NET < 4.5, proces zostanie zakończony, gdy tylko zadanie braku zostanie sfinalizowana, ponieważ masz niepostrzeżenie wyjątek.
    • W .NET> = 4.5, wyjątek zostanie po cichu upuszczony.
Powiązane problemy