2015-05-14 19 views
5

Ho un po 'di codice Sto downgrade da .NET 4.5 bella async e await parole chiave a. NET 4.0. Sto usando ContinueWith per creare una continuazione simile al modo in cui funziona await.Come prendere un OperationCanceledException quando si utilizza ContinueWith

In sostanza, il mio vecchio codice era:

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

(Come ci si potrebbe aspettare, MyStaticClass.DoStuff(token) chiama regolarmente token.ThrowIfCancellationRequested().)

mio nuovo codice simile a questo:

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

Tuttavia, lo OperationCanceledException non viene mai catturato. Cosa sta succedendo? Dove metto il mio blocco try/catch?

+5

noti che è possibile utilizzare aspettano con .NET 4.0 utilizzando https://www.nuget.org/packages/Microsoft.Bcl.Async/ –

risposta

6

La cancellazione viene gestita diversamente dalle altre eccezioni. In sostanza, è possibile utilizzare questo modello:

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

O l'alternativo:

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

Nel tuo caso, non stai cattura nulla perché:

  • Task.Factory.StartNew ritorna immediatamente e sempre riesce.
  • tuo continuazione corre sempre
  • Accesso task.Result genera AggregateException dal momento che il compito viene annullata
  • L'eccezione non è gestita da nulla dal momento che è gettato da un thread pool di thread. Ops. Che cosa succede dopo depends on the framework version:

    • In .NET < 4.5, il processo sarà terminato non appena l'operazione in mancanza è finalizzato, in quanto si ha un'eccezione inosservata.
    • In .NET> = 4.5, l'eccezione verrà ignorata.