Effizientes Mischen von Sync- und Async-Methoden in einer einzigen Methode?


11

Okay, es klingt seltsam, aber der Code ist sehr einfach und erklärt die Situation gut.

public virtual async Task RemoveFromRoleAsync(AzureTableUser user, string role)
{
    AssertNotDisposed();
    var roles = await GetRolesForUser(user);
    roles.Roles = RemoveRoles(roles.Roles, role);
    await Run(TableOperation.Replace(roles));
}

(Ich weiß, dass ich in der folgenden Zusammenfassung spreche, aber das Obige ist eine tatsächliche Methode aus dem tatsächlichen Produktionscode, die tatsächlich das tut, worüber ich hier frage, und ich bin tatsächlich daran interessiert, dass Sie sie tatsächlich überprüfen es für die Richtigkeit gegenüber dem Async / Wait-Muster.)

Ich begegne diesem Muster jetzt, wo ich async/ awaitmore benutze, immer öfter. Das Muster besteht aus der folgenden Ereigniskette:

  1. Warten Sie auf einen ersten Anruf, der mir einige Informationen liefert, an denen ich arbeiten muss
  2. Arbeiten Sie synchron an diesen Informationen
  3. Warten Sie auf einen letzten Anruf, der die aktualisierte Arbeit speichert

Der obige Codeblock beschreibt normalerweise, wie ich mit diesen Methoden umgehe. Ich habe awaitden ersten Anruf, den ich machen muss, weil er asynchron ist. Als Nächstes erledige ich die Arbeit, die ich ausführen muss, die nicht an E / A oder Ressourcen gebunden ist und daher nicht asynchron ist. Schließlich speichere ich meine Arbeit, die auch ein asyncAufruf ist, und aus Frachtkult ich awaites.

Aber ist dies der effizienteste / korrekteste Weg, um mit diesem Muster umzugehen? Mir scheint, ich könnte awaitden letzten Anruf überspringen , aber was ist, wenn er fehlschlägt? Und sollte ich eine TaskMethode verwenden, ContinueWithum meine synchrone Arbeit mit dem ursprünglichen Aufruf zu verketten? Ich bin gerade an einem Punkt angelangt, an dem ich nicht sicher bin, ob ich damit richtig umgehe.

Gibt es angesichts des Codes im Beispiel eine bessere Möglichkeit, diese Aufrufkette für asynchrone / synchrone / asynchrone Methoden zu handhaben?


Der Code erscheint mir so kurz und verständlich wie möglich. Alles, was mir einfällt, führt zu unnötiger Komplexität.
Euphoric

@Euphoric: Soll ich mich die Mühe machen, auf meinen letzten Anruf zu warten? Was würde passieren, wenn ich es nicht täte und es warf? Wäre es anders als derzeit?
Abgezockt am

Antworten:


3

Ja, ich denke, das ist der richtige Weg.

Sie können die Sekunde nicht überspringen await. Wenn Sie dies tun, scheint die Methode zu früh abgeschlossen zu sein (bevor die Entfernung tatsächlich durchgeführt wurde), und Sie würden nie herausfinden, ob die Entfernung fehlgeschlagen ist.

Ich sehe nicht, wie ContinueWith()oder so etwas hier helfen würde. Sie könnten es verwenden, um die Verwendung zu vermeiden await, aber es würde Ihren Code komplizierter und weniger lesbar machen. Und das ist der springende Punkt await: das Schreiben von asynchronem Code im Vergleich zur Verwendung von Fortsetzungen zu vereinfachen.


0

Mit diesem Muster können Sie sicherstellen, dass alle E / A asynchron sind. Synchrone E / A-Methoden bewirken, dass der aktuelle Thread blockiert wird, während er auf eine Antwort vom E / A-Ziel (Netzwerk, Dateisystem usw.) wartet.

Eine andere zu berücksichtigende Sache ist, dass awaitsie verwendet werden sollte, wenn Sie einen Rückgabewert benötigen oder wenn Sie den erwarteten Code benötigen, um fertig zu werden, bevor Sie etwas anderes tun. Wenn Sie keines dieser Dinge benötigen, können Sie Ihre asynchrone Methode mit "feuern und vergessen" Task.Run.

Für die effizienteste Nutzung von Computerressourcen RemoveRolessollten await RemoveRolesAsyncE / A- RemoveRolesAsyncVorgänge aktiviert werden, und die von aufgerufenen E / A-Methoden sollten ebenfalls asynchron sein (und möglicherweise abgewartet werden).

Wenn die Leistung nicht Ihr größtes Anliegen ist, ist es in Ordnung, synchrone E / A in einem asynchronen Thread auszuführen. Es ist jedoch eine technische Schuld. (In diesem Fall möchten Sie möglicherweise die erste asynchrone Methode mit aufrufen ConfigureAwait, je nachdem, wo der Code ausgeführt wird.)

Hier finden Sie einen detaillierten Überblick über die Best Practices: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

Hier finden Sie einige Hinweise zum Verhalten von ConfigureAwait in verschiedenen Umgebungen wie ASP.NET, WebAPI usw. - /programming/13489065/best-practice-to-call-configureawait-for-all-server-side -Code


2
Sie sollten niemals Code mit Task.Run abfeuern und vergessen. Alle Aufgaben sollten erwartet werden. Wenn Sie nicht auf die Aufgabe warten und die Aufgabe in einem Zustand, in dem die Ausnahme "nicht beobachtet" ist, mit Müll gesammelt wird, wird zur Laufzeit eine UnobservedTaskException ausgelöst, die Ihre Anwendung je nach Framework-Version zum Absturz bringen kann.
Triynko
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.