Ab C # 7.0 können asynchrone Methoden ValueTask <T> zurückgeben. Die Erklärung besagt, dass es verwendet werden sollte, wenn wir ein zwischengespeichertes Ergebnis haben oder asynchron über synchronen Code simulieren. Ich verstehe jedoch immer noch nicht, was das Problem bei der Verwendung von ValueTask immer ist oder warum async / await nicht von Anfang an mit einem Werttyp erstellt wurde. Wann würde ValueTask den Job nicht erledigen?
c#
asynchronous
Stilgar
quelle
quelle
ValueTask<T>
(in Bezug auf Zuweisungen) für tatsächlich asynchrone Vorgänge keine Ergebnisse erzielt werden (da in diesem FallValueTask<T>
noch eine Heap-Zuweisung erforderlich ist). Es geht auch darumTask<T>
, viel andere Unterstützung in Bibliotheken zu haben.Antworten:
Aus den API-Dokumenten (Hervorhebung hinzugefügt):
quelle
Task
Zuordnung gespeichert (die heutzutage klein und billig ist), jedoch auf Kosten der Vergrößerung der vorhandenen Zuordnung des Anrufers und der Verdoppelung des Rückgabewerts (Auswirkungen auf die Registerzuweisung). Obwohl es eine klare Wahl für ein gepuffertes Leseszenario ist, würde ich nicht empfehlen, es standardmäßig auf alle Schnittstellen anzuwenden.Task
oderValueTask
kann als synchroner Rückgabetyp (mitTask.FromResult
) verwendet werden. Aber es gibt immer noch einen Wert (heh),ValueTask
wenn Sie etwas haben, von dem Sie erwarten , dass es synchron ist.ReadByteAsync
ein klassisches Beispiel sein. Ich glaube, esValueTask
wurde hauptsächlich für die neuen "Kanäle" (Low-Level-Byte-Streams) erstellt, die möglicherweise auch im ASP.NET-Kern verwendet werden, wo die Leistung wirklich wichtig ist.Task<T>
als Standard zu verwenden. Dies liegt nur daran, dass die meisten Entwickler mit den Einschränkungen nicht vertraut sindValueTask<T>
(insbesondere die Regel "Nur einmal verbrauchen" und die Regel "Keine Blockierung"). Das heißt, wenn alle Entwickler in Ihrem Team sind gut mitValueTask<T>
, dann würde ich ein empfehlen Teamebene Leitlinie vorziehtValueTask<T>
.Strukturtypen sind nicht frei. Das Kopieren von Strukturen, die größer als die Größe einer Referenz sind, kann langsamer sein als das Kopieren einer Referenz. Das Speichern von Strukturen, die größer als eine Referenz sind, benötigt mehr Speicher als das Speichern einer Referenz. Strukturen, die größer als 64 Bit sind, werden möglicherweise nicht registriert, wenn eine Referenz registriert werden kann. Die Vorteile eines geringeren Sammeldrucks dürfen die Kosten nicht überschreiten.
Leistungsprobleme sollten mit einer technischen Disziplin angegangen werden. Machen Sie Ziele, messen Sie Ihren Fortschritt anhand von Zielen und entscheiden Sie dann, wie Sie das Programm ändern, wenn die Ziele nicht erreicht werden. Messen Sie dabei, ob Ihre Änderungen tatsächlich Verbesserungen sind.
await
wurde zu C # hinzugefügt, lange nachdem derTask<T>
Typ bereits vorhanden war. Es wäre etwas pervers gewesen, einen neuen Typ zu erfinden, wenn es ihn bereits gab. Undawait
er hat sehr viele Design-Iterationen durchlaufen, bevor er sich für die entschieden hat, die 2012 ausgeliefert wurde. Das Perfekte ist der Feind des Guten; Es ist besser, eine Lösung zu liefern, die gut mit der vorhandenen Infrastruktur zusammenarbeitet, und dann bei Bedarf der Benutzer später Verbesserungen vorzunehmen.Ich stelle außerdem fest, dass die neue Funktion, vom Benutzer bereitgestellte Typen als Ausgabe einer vom Compiler generierten Methode zuzulassen, ein erhebliches Risiko und einen erheblichen Testaufwand mit sich bringt. Wenn die einzigen Dinge, die Sie zurückgeben können, nichtig oder eine Aufgabe sind, muss das Testteam kein Szenario berücksichtigen, in dem ein absolut verrückter Typ zurückgegeben wird. Das Testen eines Compilers bedeutet, nicht nur herauszufinden, welche Programme wahrscheinlich geschrieben werden, sondern auch, welche Programme geschrieben werden können , da der Compiler alle legalen Programme kompilieren soll, nicht nur alle sinnvollen Programme. Das ist teuer.
Der Zweck der Sache ist eine verbesserte Leistung. Es macht den Job nicht, wenn es die Leistung nicht messbar und signifikant verbessert. Es gibt keine Garantie dafür.
quelle
ValueTask<T>
ist keine Untermenge vonTask<T>
, es ist eine Obermenge .Sie können also eine Methode schreiben, die entweder asynchron oder synchron ist, anstatt für jede Methode eine ansonsten identische Methode zu schreiben. Sie könnten es überall verwenden,
Task<T>
aber es würde oft nichts hinzufügen.Nun, es fügt eines hinzu: Es fügt dem Aufrufer ein implizites Versprechen hinzu, dass die Methode tatsächlich die zusätzlichen Funktionen verwendet, die sie
ValueTask<T>
bereitstellt. Ich persönlich bevorzuge die Auswahl von Parameter- und Rückgabetypen, die dem Anrufer so viel wie möglich mitteilen. Geben Sie nicht zurück,IList<T>
wenn die Aufzählung keine Zählung liefern kann. kehre nicht zurück,IEnumerable<T>
wenn es geht. Ihre Verbraucher sollten keine Dokumentation nachschlagen müssen, um zu wissen, welche Ihrer Methoden vernünftigerweise synchron aufgerufen werden können und welche nicht.Ich sehe zukünftige Designänderungen dort nicht als überzeugendes Argument. Im Gegenteil: Wenn eine Methode ihre Semantik ändert, sollte sie den Build unterbrechen, bis alle Aufrufe entsprechend aktualisiert wurden. Wenn dies als unerwünscht angesehen wird (und glauben Sie mir, ich bin mit dem Wunsch einverstanden, den Build nicht zu brechen), ziehen Sie die Versionierung der Benutzeroberfläche in Betracht.
Dies ist im Wesentlichen das, wofür starkes Tippen gedacht ist.
Wenn einige der Programmierer, die in Ihrem Shop asynchrone Methoden entwerfen, keine fundierten Entscheidungen treffen können, kann es hilfreich sein, jedem dieser weniger erfahrenen Programmierer einen leitenden Mentor zuzuweisen und eine wöchentliche Codeüberprüfung durchzuführen. Wenn sie falsch raten, erklären Sie, warum dies anders gemacht werden sollte. Es ist Overhead für die Senioren, aber es wird die Junioren viel schneller auf den neuesten Stand bringen, als sie nur in die Tiefe zu werfen und ihnen eine willkürliche Regel zu geben, der sie folgen müssen.
Wenn der Typ, der die Methode geschrieben hat, nicht weiß, ob sie synchron aufgerufen werden kann, wer auf der Erde tut das?!
Wenn Sie so viele unerfahrene Programmierer haben, die asynchrone Methoden schreiben, rufen dieselben Leute sie auch auf? Sind sie qualifiziert, selbst herauszufinden, welche sicher als asynchron bezeichnet werden können, oder wenden sie eine ähnlich willkürliche Regel an, wie sie diese Dinge nennen?
Das Problem hierbei sind nicht Ihre Rückgabetypen, sondern Programmierer, denen Rollen zugewiesen werden, für die sie nicht bereit sind. Das muss aus einem bestimmten Grund geschehen sein, daher kann es sicher nicht trivial sein, das Problem zu beheben. Es zu beschreiben ist sicherlich keine Lösung. Aber nach einer Möglichkeit zu suchen, das Problem am Compiler vorbei zu schleichen, ist auch keine Lösung.
quelle
ValueTask<T>
, gehe ich davon aus, dass der Typ, der die Methode geschrieben hat, dies getan hat, da die Methode tatsächlich die hinzugefügte Funktionalität verwendetValueTask<T>
. Ich verstehe nicht, warum Sie es für wünschenswert halten, dass alle Ihre Methoden denselben Rückgabetyp haben. Was ist das Ziel dort?object
weil Sie vielleicht eines Tages möchten, dass sieint
statt zurückkehrenstring
.Es gibt einige Änderungen in .Net Core 2.1 . Ab .net Core 2.1 kann ValueTask nicht nur die synchron abgeschlossenen Aktionen darstellen, sondern auch die abgeschlossene Asynchronisierung. Zusätzlich erhalten wir nicht generischen
ValueTask
Typ.Ich werde Stephen Toub einen Kommentar hinterlassen , der sich auf Ihre Frage bezieht:
Die Funktion kann nicht nur im .net Core 2.1 verwendet werden. Sie können es mit dem System.Threading.Tasks.Extensions- Paket verwenden.
quelle