2011-08-11 10 views
6

Mam krótkie zadanie asynchroniczne, które często trzeba będzie anulować po uruchomieniu. Klasa "Zadanie" ma wskaźnik IsCanceled, który, jak sądzę, byłby wygodny do użycia w celu wskazania, że ​​zadanie asynchroniczne zostało anulowane bez uruchamiania do ukończenia, ale o ile mogę powiedzieć, jedynym sposobem na to, by zadanie asynchroniczne zostało oznaczone jako anulowane, jest rzucić wyjątek TaskCanceledException w funkcji asynchronicznej. Rzucanie wyjątku rutynowo, w celu wskazania sytuacji, która następuje nieświadomie, jest sprzeczne z tym, jak rozumiem, że należy stosować wyjątki. Czy ktokolwiek wie lepszy sposób wskazania zadania asynchronicznego, które ma zostać anulowane, gdy oczekuje się, że będzie się często zdarzało?C# async CTP - Jak oznaczyć zadanie asynchroniczne jako anulowane bez wywoływania wyjątku TaskCanceledException?

Moim następnym najlepszym rozwiązaniem jest, aby powrócić strukturę, która ma własną nieruchomość IsCanceled:

(mam ignorowane kilka dobrych kodowania i styl praktyki dla zwięzłości tutaj)

class MightBeCanceled<T> 
{ 
    public readonly T Value; 
    public readonly bool IsCanceled; 

    public MightBeCanceled(T value) { Value = value; IsCanceled = false; } 
    public static MightBeCanceled<T> Canceled = new MightBeCanceled<T>(default(T), true); 
    private MightBeCanceled(T value, bool isCanceled) { Value = value; IsCanceled = isCanceled; } 
} 

... 

static async Task<MightBeCanceled<int>> Foo() 
{ 
    if (someCancellationCondition) 
     return MightBeCanceled<int>.Canceled; 
    else 
     return new MightBeCanceled<int>(42); 
} 

static async void Bar() 
{ 
    var mightBeCanceled = await Foo(); 

    if (mightBeCanceled.IsCanceled) 
     ; // Take canceled action 
    else 
     ; // Take normal action 
} 

ale to wydaje zbędne i trudniejsze w użyciu. Nie wspominając o tym, że wprowadza problemy z konsekwencją, ponieważ będą dwa IsCanceled (jeden w zadaniu i jeden w MightBeCanceled).

+1

Kto anuluje Foo? Powinien być dostępny do anulowania tylko przez osobę dzwoniącą za pomocą Token Anulowania –

Odpowiedz

2

Problem polega na tym, że wyjątek pozostanie niezauważony, dopóki nie zaczekasz na niego. Tak więc akt wywoływania await oznacza, że ​​w końcu zauważysz wyjątek w pewnym momencie, gdy zwracana jest wartość końcowa lub jeśli czekasz na zakończenie zadania. Jednakże, jeśli nigdy nie dbasz o to (jest to zadanie polegające na pożarze i zapomnieniu), to wyjątek nie dotyczy problemu (w przeważającej części).

Ja też uważam, że to trochę dziwne, zawsze trzeba spróbować/złapać zadanie, aby obsłużyć zagregowane wyjątki. Zastanów się jednak trochę:

try 
{ 
    var myResult = await Foo(); 

    // Do Success Actions Here... 
} 
catch(AggregateException e) 
{ 
    e.Flatten().Handle(ex => 
     { 
      if(ex is OperationCanceledException) 
      { 
       // Do Canceled Thing Here 
       return true; 
      } 

      return false; 
     }); 
} 

Nie jest to zbyt daleko. Pod wieloma względami myślę o anulowaniu kolejnego zadania i jak mam to zrobić? ThreadAbortException? Nie wydaje się tak daleko, aby po prostu wyrzucić konkretny wyjątek dotyczący anulowania.

To jest prawie ten sam wzór, który widziałem w wielu miejscach, jak radzić sobie z anulowaniem.

2

Zazwyczaj celem anulowania jest, aby wywoływacz asynchronicznego działania poinformował akcję, że powinien przerwać przetwarzanie. W tym celu należy przekazać CancellationToken do metody asynchronicznej. Właśnie dlatego oczekiwanie na zadanie, które ustawia IsCancelled, rzuca się. To był wyjątkowy sygnał dla sprowokowanej zewnętrznie akcji. Zadanie Anulowanie nie powinno być używane do sterowania przepływem, ale tylko po to, aby dać asynchronicznej akcji opcję zakończenia wcześnie, jeśli strona oczekująca zasygnalizuje, że nie chce już wyniku.

Jeśli działania asynchroniczne są anulowane wewnętrznie, już przeładowałeś koncepcję i powiedziałbym, że to odzwierciedlające tę różnicę w anulowaniu jest warte zachodu i powinno być właściwością na pojemniku wynikowym, podobnie jak proponowałeś. Ale zamiast nazywać to MightBeCancelled<T>, może coś jak InternallyCancellableResult<T>, aby odzwierciedlić różnicę koncepcji anulowania.

Powiązane problemy