it-swarm.com.de

Warten Sie auf eine abgeschlossene Aufgabe, die mit der Aufgabe identisch ist.Ergebnis?

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?

102
julio.g

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:

  1. Asynchroner Code in einer Anwendung kann nur await verwenden.
  2. Asynchroner Dienstprogrammcode (in einer Bibliothek) kann gelegentlich Result/Wait verwenden, wenn der Code dies wirklich erfordert. Eine solche Verwendung sollte wahrscheinlich Kommentare haben.
  3. Paralleler Taskcode kann 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.

142
Stephen Cleary

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.

11
noseratio