2011-12-30 14 views
23

Mam metodę, która tworzy niektóre zadania, a następnie czeka na nich z WaitAll przed powrotem. Problem polega na tym, że jeśli te zadania zostały anulowane, to WaitAll wyrzuci AggregateException zawierający partie TaskCanceledException.Jak mogę czekać na zadania bez rzucania wyjątków TaskCanceledExceptions?

Oznacza to, że WaitAll rzuci wyjątków w dwóch różnych okolicznościach:

  • Wyjątki, które wskazują prawdziwego błędu. Oznacza to, że był warunek, którego nie znaliśmy; muszą propagować jako nieobsługiwane wyjątki, dopóki ostatecznie nie zakończą procesu.
  • Wyjątki wskazujące, że użytkownik kliknął przycisk Anuluj. Oznacza to, że zadanie zostało anulowane i wyczyszczone, a program powinien nadal działać normalnie.

jest montowany prostopadle w definicji vexing exception: to wyjątek wrzucony zupełnie nie wyjątkowych okolicznościach, więc złapać ją, aby powrócić do normalnego przepływu sterowania. Na szczęście łatwo go złapać, prawda? Po prostu dodaj catch (AggregateException) i - och, czekaj, to ten sam typ, który zostanie zgłoszony, gdy wystąpi błąd krytyczny.

Muszę poczekać, aż zadania zakończą się, zanim wrócę (muszę wiedzieć, że nie używają już swoich połączeń z bazami danych, uchwytów plików ani niczego innego), więc potrzebuję WaitAll lub czegoś takiego podobny. A jeśli jedno z zadań zostało zarzucone, chcę, aby te wyjątki były propagowane jako nieobsługiwane wyjątki. Po prostu nie chcę wyjątków do anulowania.

W jaki sposób mogę zapobiec dodawaniu wyjątków w przypadku anulowanych zadań przez użytkownika WaitAll?

+0

użyć przeciążenie, że trwa CancellationToken. –

+0

@HansPassant, dokumenty dla tego przeciążenia w rzeczywistości nie mówią tego, do czego używa tokena. Czy zignoruje TaskCanceledExceptions powiązane z tym tokenem, czy też po prostu powróci wcześniej, jeśli token zostanie anulowany? Nie potrzebuję powrotu, dopóki wszystkie zadania nie przestaną działać. –

+1

Do anulowania zadań służy znacznik Anulowanie. I odfiltruj specyficzny wyjątek OperationCancelException, który teraz otrzymasz. –

Odpowiedz

28

Metoda AggregateException udostępnia metodę , która może być używana w takich sytuacjach. Jeśli na przykład chcesz ignorować TaskCanceledException można zrobić:

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

jeśli wszystkie wyjątki są typu TaskCanceledException metoda Handle nie rzuci żadnego wyjątku; w przeciwnym razie zostanie wygenerowany nowy AggregateException zawierający tylko nieobsłużone wyjątki.

1

podstawie João Angelo's suggestion, tu idzie o Zadanie klasy rozszerzenia

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

    } 
} 
Powiązane problemy