2015-07-21 10 views
5

prega, osservare il seguente codice banale:Perché il Task.WhenAny non lancia un TimeoutException previsto?

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

esecuzione emette:

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

nota, non scaduta messaggio.

Ora, se sostituisco Task.WhenAny con Task.WhenAll qui è quello che ottengo:

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

nota la presenza del scaduta messaggio questa volta.

E, se mi tolgo la Task.WhenAll involucro a tutti e chiamare direttamente RunAsync:

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

Il Timeout messaggio è lì, come previsto.

Quindi, qual è l'accordo con Task.WhenAny? Ovviamente interrompe il metodo asincrono, ma dov'è lo TimeoutException?

+1

Perché stai usando '.Task.GetAwaiter() .GetResult() ', in primo luogo è equivoco a' Task.Result' e in secondo luogo si rischia di essere mangiato da un grue. – Aron

+1

@Aron Non è equivalente. Genera l'eccezione reale. Ma sì, sta ancora bloccando e potrebbe portare a deadlock. – i3arnon

+0

[Task.getAwaiter()] (https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.getawaiter (v = vs.110) .aspx): 'Questo metodo è destinato per l'uso del compilatore piuttosto che per l'uso nel codice dell'applicazione. – sstan

risposta

12

Task.WhenAny non rigenerare eccezioni alle singole attività (a differenza Task.WhenAll):

"Il compito tornato completerà quando uno dei compiti in dotazione ha completato il compito tornato terminerà sempre nello stato RanToCompletion. con il suo Result impostato sulla prima attività da completare. Ciò vale anche se la prima attività da completare termina nello stato Canceled o Faulted ".

Da Task.WhenAny

Ciò significa che completerà con successo non importa quale senza alcun tipo di eccezioni.

Per rigenerare in realtà l'eccezione della singola attività completata è necessario await il compito tornato in sé:

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

o nel vostro caso:

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

Oh uomo, restituisce un 'Compito '. Ora ha senso, ma perché ha un tipo di ritorno così strano? – mark

+2

@mark L'attività esterna è lì per poterla attendere.il compito interno è lì per te per ottenere il primo compito completato come risultato. Quando si utilizza 'Task.WhenAny', probabilmente si desidera sapere quale attività è stata completata per prima, o qual è il suo risultato. – i3arnon

Problemi correlati