2011-12-30 13 views
23

Ho un metodo che crea alcune attività e quindi le attende con WaitAll prima di tornare. Il problema è che se queste attività sono state annullate, WaitAll genera un AggregateException contenente un sacco di TaskCanceledException s.Come posso attendere le attività senza lanciare TaskCanceledExceptions?

Ciò significa che WaitAll getterà le eccezioni in due diverse circostanze:

  • eccezioni che indicano un errore in buona fede. Ciò significa che c'era una condizione che non sapevamo come gestire; devono propagarsi come eccezioni non gestite, fino a quando non terminano il processo.
  • Eccezioni che indicano che l'utente ha fatto clic su un pulsante Annulla. Ciò significa che l'attività è stata annullata e pulita, e il programma dovrebbe continuare a funzionare normalmente.

Quest'ultimo rientra perfettamente nella definizione di un vexing exception: si tratta di un'eccezione gettato in una circostanza del tutto non eccezionale, quindi devo prenderlo per riprendere il normale flusso di controllo. Fortunatamente è facile da catturare, giusto? Basta aggiungere catch (AggregateException) e - oh aspetta, è lo stesso tipo che viene lanciato quando si verifica un errore fatale.

Ho bisogno di aspettare che i task finiscano di funzionare prima di tornare (devo sapere che non usano più le loro connessioni al database, handle di file o altro), quindi ho bisogno di WaitAll o qualcosa del genere simile. E se qualcuno dei compiti è in errore, desidero che tali eccezioni si propagino come eccezioni non gestite. Non voglio eccezioni per la cancellazione.

Come impedire a WaitAll di generare eccezioni per le attività annullate?

+0

Utilizzare l'overload che accetta un CancellationToken. –

+0

@HansPassant, i documenti per quel sovraccarico in realtà non dicono per cosa utilizza il token. Ignorerà TaskCanceledExceptions associate a quel token, o ritornerà semplicemente in anticipo se il token viene cancellato? Ho bisogno di non tornare fino a quando tutte le attività non si sono fermate. –

+1

È possibile utilizzare CancellationToken per annullare le attività. E filtra la specifica OperationCancelException che ora otterrai. –

risposta

28

Il AggregateException fornisce un metodo Handle che può essere utilizzato per queste situazioni. Se per esempio si vuole ignorare TaskCanceledException si può fare:

var all = new AggregateException(
    new NullReferenceException(), 
    new TaskCanceledException(), 
    new TaskCanceledException(), 
    new InvalidOperationException(), 
    new TaskCanceledException()); 

try 
{ 
    throw all; 
} 
catch (AggregateException errors) 
{ 
    errors.Handle(e => e is TaskCanceledException); 
} 

Se tutte le eccezioni sono di tipo TaskCanceledException, il metodo Handle non lancerà alcuna eccezione; altrimenti verrà lanciato un nuovo AggregateException contenente solo le eccezioni non gestite.

1

Sulla base di João Angelo's suggestion, qui va un Task classe estensione

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading; 
using System.Threading.Tasks; 

namespace MySharedLibrary.Extensions 
{ 
    public static class TaskExtensions 
    { 

     // This code is based João Angelo's stackoverflow suggestion https://stackoverflow.com/a/8681687/378115 

     // Use this when a CancellationTokenSource is used 
     public static void SafeWait(this Task TargetTask, CancellationTokenSource TargetTaskCancellationTokenSource) 
     { 
      if (TargetTaskCancellationTokenSource.IsCancellationRequested == false) 
      { 
       TargetTaskCancellationTokenSource.Cancel(); 
      } 
      SafeWait(TargetTask); 
     } 

     // Use this when no CancellationTokenSource is used 
     public static void SafeWait(this Task TargetTask) 
     { 
      try 
      { 
       if (TargetTask.IsCanceled == false) 
       { 
        TargetTask.Wait(); 
       } 
      } 
      catch (AggregateException errors) 
      { 
       errors.Handle(e => e is TaskCanceledException); 
      } 
     } 

    } 
} 
Problemi correlati