Cattura delle eccezioni sulle operazioni asincrone

Sto leggendo di più su async qui: http://msdn.microsoft.com/en-us/library/hh873173(v=vs.110).aspx

Passando attraverso questo esempio:

Task [] recommendations = …; while(recommendations.Count > 0) { Task recommendation = await Task.WhenAny(recommendations); try { if (await recommendation) BuyStock(symbol); break; } catch(WebException exc) { recommendations.Remove(recommendation); } } 

Mi chiedo, se sto già aspettando l’esecuzione su Task.WhenAny perché devo aspettare di nuovo all’interno del blocco try?

Se l’ho già fatto: Task recommendation = await Task.WhenAny(recommendations); Perché fare questo: if (await recommendation) BuyStock(symbol);

La prima await esiste per attendere in modo asincrono il completamento della prima attività (es. recommendation ). La seconda await è solo lì per estrarre il risultato effettivo dall’attività già completata e generare eccezioni memorizzate nell’attività. ( è importante ricordare che l’attesa di un’attività completata è ottimizzata e verrà eseguita in modo sincrono ).

Un’opzione diversa per ottenere il risultato sarebbe l’utilizzo di Task.Result , tuttavia differisce nel modo in cui gestisce le eccezioni. await WebException eccezione effettiva (es. WebException ) mentre Task.Result genererebbe Task.Result AggregateException contenente l’eccezione effettiva all’interno.

 Task [] recommendations = …; while(recommendations.Count > 0) { Task recommendation = await Task.WhenAny(recommendations); try { if (recommendation.Result) { BuyStock(symbol); } break; } catch(AggregateException exc) { exc = exc.Flatten(); if (exc.InnerExceptions[0] is WebException) { recommendations.Remove(recommendation); } else { throw; } } } 

Chiaramente attendere l’attività è più semplice e quindi è il modo consigliato di recuperare un risultato da un’attività.

L’uso dell’attesa qui crea la semantica di gestione degli errori desiderata. Se ha usato Result invece di await allora AggregateException sarebbe stato riproiettato direttamente; quando si utilizza await la prima eccezione all’interno di AggregateException sia estratta, viene restituita quell’eccezione. Cancellare l’autore di questo codice voleva che venisse lanciata la WebException , piuttosto che una AggregateException che avrebbe bisogno di scartare manualmente.

Avrebbe potuto usare un altro approccio, certo. Questo era semplicemente l’approccio che l’autore del codice preferiva, in quanto gli permetteva di scrivere il codice più come il tradizionale codice sincrono piuttosto che cambiare radicalmente lo stile del codice.

Hai ragione. Non è necessario. Puoi sostituirlo con

 if (recommendation.Result) BuyStock(symbol); 

Si noti inoltre che l’ await non attenderà (non imposterà la continuazione) quando viene data l’attività completata. Si eseguirà in modo sincrono in quel caso come ottimizzazione. Immagino che l’autore sfrutti questa ottimizzazione.

Se chiedi perché l’autore ha scritto in quel modo, può essere la coerenza? solo lui lo sa !.

Se ho già fatto questo: Raccomandazione attività = attendere Task.WhenAny (consigli); Perché fare questo: se (attendere la raccomandazione) BuyStock (simbolo);

Perché Task.WhenAny restituisce un’attività Task> e si desidera scartare l’outter Task per recuperare il bool risultante. Si potrebbe fare lo stesso accedendo alla proprietà Task.Result dell’attività restituita

Poiché Task.WhenAny(IEnumerable> tasks) restituisce un’attività Task> . L’attività esterna (quella creata dalla chiamata Task.WhenAny ) verrà completata quando una delle attività passate a esso viene completata con l’attività completata.

Altre risposte hanno indicato che è necessario await l’attività restituita await Task.WhenAll per await Task.WhenAll il valore restituito (in alternativa, è ansible utilizzare la proprietà Result ).

Tuttavia, puoi anche eliminare il tuo try / catch (ed è una buona cosa evitare di rilevare eccezioni inutili)

 Task recommendation = await Task.WhenAny(recommendations); if(!recommendation.IsFaulted) { if (await recommendation) BuyStock(symbol); break; } else { if(recommendation.Exception.InnerExceptions[0] is WebException) { recommendations.Remove(recommendation); } else { throw recommendation.Exception.InnerExceptions[0]; } }