Obecnie czytam książkę „ Współbieżność w C # Cookbook ” autorstwa Stephena Cleary'ego i zauważyłem następującą technikę:
var completedTask = await Task.WhenAny(downloadTask, timeoutTask);
if (completedTask == timeoutTask)
return null;
return await downloadTask;
downloadTask
jest wezwaniem do httpclient.GetStringAsync
i timeoutTask
wykonuje Task.Delay
.
W przypadku, gdy nie upłynął limit czasu, downloadTask
jest już zakończony. Dlaczego trzeba czekać sekundę zamiast wracać downloadTask.Result
, skoro zadanie zostało już wykonane?
c#
asynchronous
async-await
task
julio.g
źródło
źródło
downloadTask
itimeoutTask
? Co oni robią?AggregateException
zResult
pierwszym wyjątkiem przezExceptionDispatchInfo
withawait
). Bardziej szczegółowo omówione w artykule Stephena Toub „Task Exception Handling in .NET 4.5”: blogs.msdn.com/b/pfxteam/archive/2011/09/28/… )Odpowiedzi:
Jest tu już kilka dobrych odpowiedzi / komentarzy, ale tylko po to, aby zadzwonić ...
Istnieją dwa powody, dlaczego wolę
await
ponadResult
(lubWait
). Po pierwsze, obsługa błędów jest inna;await
nie zawija wyjątku wAggregateException
. Idealnie byłoby, gdyby kod asynchroniczny nigdy nie musiał mieć do czynieniaAggregateException
, chyba że specjalnie tego chce .Drugi powód jest nieco bardziej subtelny. Jak opisuję na moim blogu (iw książce),
Result
/Wait
może powodować zakleszczenia i może powodować jeszcze bardziej subtelne zakleszczenia, gdy jest używany wasync
metodzie . Tak więc, kiedy czytam kod i widzęResult
lubWait
, jest to natychmiastowa flaga ostrzegawcza. ZnakResult
/Wait
jest poprawny tylko wtedy, gdy masz absolutną pewność, że zadanie zostało już wykonane. Jest to nie tylko trudne do zobaczenia na pierwszy rzut oka (w kodzie świata rzeczywistego), ale jest też bardziej kruche w przypadku zmian w kodzie.Nie oznacza to, że
Result
/ nieWait
powinno się nigdy używać. Postępuję zgodnie z tymi wytycznymi w moim własnym kodzie:await
.Result
/,Wait
jeśli kod naprawdę tego wymaga. Takie użycie prawdopodobnie powinno mieć komentarze.Result
iWait
.Zauważ, że (1) jest zdecydowanie częstym przypadkiem, stąd moja tendencja do używania
await
wszędzie i traktowania innych przypadków jako wyjątków od ogólnej reguły.źródło
await
zapobiegaAggregateException
opakowaniu.AggregateException
został zaprojektowany do programowania równoległego, a nie asynchronicznego.Wait
było przyłączenie się do instancji Dynamic Task ParallelismTask
. Używanie go do oczekiwania naTask
wystąpienia asynchroniczne jest niebezpieczne. Microsoft rozważał wprowadzenie nowego typu „Obietnicy”, aleTask
zamiast tego zdecydował się użyć istniejącego ; Kompromisem z ponownego wykorzystania istniejącegoTask
typu do zadań asynchronicznych jest to, że otrzymujesz kilka interfejsów API, które po prostu nie powinny być używane w kodzie asynchronicznym.Ma to sens, jeśli
timeoutTask
jest produktem, zTask.Delay
którego wierzę, że jest w książce.Task.WhenAny
zwracaTask<Task>
, gdzie wewnętrzne zadanie jest jednym z tych, które przekazałeś jako argumenty. Można to przepisać w ten sposób:W obu przypadkach, ponieważ
downloadTask
zostało już zakończone, istnieje niewielka różnica międzyreturn await downloadTask
ireturn downloadTask.Result
. Chodzi o to, że ta ostatnia rzuci,AggregateException
która otacza każdy oryginalny wyjątek, jak wskazał @KirillShlenskiy w komentarzach. Ten pierwszy po prostu ponownie wyrzuciłby oryginalny wyjątek.W obu przypadkach, gdziekolwiek obsługujesz wyjątki, powinieneś sprawdzić,
AggregateException
czy nie ma wyjątków wewnętrznych, aby znaleźć przyczynę błędu.źródło