2015-07-21 19 views
5

Proszę zauważyć następujący kod trywialny:Dlaczego Task.henAny nie rzuca oczekiwany TimeoutException?

class Program 
{ 
    static void Main() 
    { 
     var sw = new Stopwatch(); 
     sw.Start(); 
     try 
     { 
      Task.WhenAny(RunAsync()).GetAwaiter().GetResult(); 
     } 
     catch (TimeoutException) 
     { 
      Console.WriteLine("Timed out"); 
     } 
     Console.WriteLine("Elapsed: " + sw.Elapsed); 
     Console.WriteLine("Press Enter to exit"); 
     Console.ReadLine(); 
    } 

    private static async Task RunAsync() 
    { 
     await Observable.StartAsync(async ct => 
     { 
      for (int i = 0; i < 10; ++i) 
      { 
       await Task.Delay(500, ct); 
       Console.WriteLine("Inside " + i); 
      } 
      return Unit.Default; 
     }).Timeout(TimeSpan.FromMilliseconds(1000)); 
    } 
} 

Running wyprowadza:

Inside 0 
Inside 1 
Elapsed: 00:00:01.1723818 
Press Enter to exit 

Uwaga, nie Timed out wiadomość.

Teraz, jeśli mogę wymienić Task.WhenAny z Task.WhenAll tutaj jest to, co mam:

Inside 0 
Inside 1 
Timed out 
Elapsed: 00:00:01.1362188 
Press Enter to exit 

Uwaga obecność Timed out komunikat ten czas.

A jeśli usunąć Task.WhenAll opakowanie w ogóle i nazywają RunAsync bezpośrednio:

Inside 0 
Inside 1 
Timed out 
Elapsed: 00:00:01.1267617 
Press Enter to exit 

Timed out wiadomość jest tam, jak oczekiwano.

Więc jaka jest umowa z Task.WhenAny? To oczywiście przerywa metodę asynchroniczną, ale gdzie jest TimeoutException?

+1

Dlaczego używasz '.Task.GetAwaiter() .GetResult() ', po pierwsze jest to równoważne' Task.Result', a po drugie możesz być zjedzony przez grue. – Aron

+1

@Aron To nie jest odpowiednik. Zgłasza faktyczny wyjątek. Ale tak, nadal blokuje i może prowadzić do zakleszczeń. – i3arnon

+0

[Task.getAwaiter()] (https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.getawaiter (v = vs.110) .aspx): 'Ta metoda jest przeznaczona do użytku kompilatora, a nie do użycia w kodzie aplikacji. " – sstan

Odpowiedz

12

Task.WhenAny nie rethrow wyjątki od poszczególnych zadań (w przeciwieństwie Task.WhenAll):

„Zwrócona zadanie zakończy, kiedy którykolwiek z dostarczonych zadań zakończyła Zwracany zadaniem będzie zawsze kończy się w stanie RanToCompletion. z jego Result ustawionym na pierwsze zadanie do wykonania.To jest prawdą, nawet jeśli pierwsze zadanie do ukończenia zakończyło się stanem Canceled lub Faulted. "

Od Task.WhenAny

to oznacza, że ​​będzie ona zakończyć się pomyślnie bez względu na to, co bez jakiegokolwiek rodzaju wyjątków.

Aby rzeczywiście przekaż wyjątek indywidualnego zakończone zadania trzeba await wracającego zadanie samo:

var completedTask = await Task.WhenAny(tasks); // no exception 
await completedTask; // possible exception 

lub w przypadku:

Task.WhenAny(RunAsync()).GetAwaiter().GetResult().GetAwaiter().GetResult(); 
+0

Oh człowiek, zwraca 'Zadanie '. Teraz ma sens, ale dlaczego ma tak dziwny typ zwrotu? – mark

+2

@mark Zewnętrzne zadanie jest dostępne, więc możesz na nie czekać.wewnętrzne zadanie polega na tym, aby uzyskać pierwsze ukończone zadanie w wyniku. Gdy używasz 'Task.WhenAny', prawdopodobnie chcesz wiedzieć, które zadanie zostało zakończone jako pierwsze lub jaki jest jego wynik. – i3arnon

Powiązane problemy