Ich lese gerade "Concurrency in C # Cookbook" von Stephen Cleary und habe die folgende Technik bemerkt:
var completedTask = await Task.WhenAny(downloadTask, timeoutTask);
if (completedTask == timeoutTask)
return null;
return await downloadTask;
downloadTask
ist ein Aufruf von httpclient.GetStringAsync
und timeoutTask
führt Task.Delay
aus.
Falls keine Zeitüberschreitung aufgetreten ist, ist downloadTask
bereits abgeschlossen. Warum muss statt der Rückgabe von downloadTask.Result
Eine Sekunde gewartet werden, da die Aufgabe bereits abgeschlossen ist?
Es gibt hier bereits einige gute Antworten/Kommentare, aber nur um sich einzuloggen ...
Es gibt zwei Gründe, warum ich await
vor Result
(oder Wait
) ziehe. Das erste ist, dass die Fehlerbehandlung anders ist; await
schließt die Ausnahme nicht in ein AggregateException
ein. Im Idealfall sollte sich asynchroner Code niemals mit AggregateException
befassen müssen, es sei denn, er möchte ausdrücklich .
Der zweite Grund ist etwas subtiler. Wie ich in meinem Blog (und im Buch) beschreibe, können Result
/Wait
Deadlocks verursachen und können bei Verwendung noch subtilere Deadlocks verursachen) in einer async
Methode . Wenn ich also Code durchlese und ein Result
oder Wait
sehe, ist das eine sofortige Warnmeldung. Das Result
/Wait
ist nur korrekt, wenn Sie absolut sicher sind , dass die Aufgabe bereits abgeschlossen ist. Dies ist nicht nur auf einen Blick schwer zu erkennen (im Code der realen Welt), sondern auch spröder bei Codeänderungen.
Das heißt nicht, dass Result
/Wait
niemals verwendet werden sollte . Ich befolge diese Richtlinien in meinem eigenen Code:
await
verwenden.Result
/Wait
verwenden, wenn der Code dies wirklich erfordert. Eine solche Verwendung sollte wahrscheinlich Kommentare haben.Result
und Wait
verwenden.Beachten Sie, dass (1) bei weitem der übliche Fall ist, daher meine Tendenz, await
überall zu verwenden und die anderen Fälle als Ausnahmen von der allgemeinen Regel zu behandeln.
Dies ist sinnvoll, wenn timeoutTask
ein Produkt von Task.Delay
Ist, von dem ich glaube, dass es in dem Buch steht.
Task.WhenAny
Gibt Task<Task>
Zurück, wobei die innere Aufgabe eine der Aufgaben ist, die Sie als Argumente übergeben haben. Es könnte so umgeschrieben werden:
Task<Task> anyTask = Task.WhenAny(downloadTask, timeoutTask);
await anyTask;
if (anyTask.Result == timeoutTask)
return null;
return downloadTask.Result;
In beiden Fällen besteht, da downloadTask
bereits abgeschlossen wurde, ein sehr geringer Unterschied zwischen return await downloadTask
Und return downloadTask.Result
. Es liegt darin, dass letzterer AggregateException
auslöst, was jede ursprüngliche Ausnahme umschließt, wie von @KirillShlenskiy in den Kommentaren hervorgehoben. Ersteres würde nur die ursprüngliche Ausnahme erneut auslösen.
In beiden Fällen sollten Sie, wo immer Sie Ausnahmen behandeln, ohnehin nach AggregateException
und seinen inneren Ausnahmen suchen, um die Fehlerursache zu ermitteln.