|
|
Veröffentlicht am 17.03.2022, 11:21:50
|
|
|
|

Die Verwendung von Task- oder Task-Klassen hat einen Performance-Engpass, den wir in früheren Artikeln nicht erwähnt haben. Kurz gesagt, diese Kurse führen an, wenn die Ergebnisse sofort verfügbar sindUnnötige Zuteilung。 Das bedeutet, dass immer ein neues Task oder Task-Objekt erstellt wird, selbst wenn das Ergebnis bereits verfügbar ist. Wir haben erwähnt, dass das asynchrone/await-Konzept, das wir in früheren Artikeln verwendet haben, schon seit der Veröffentlichung von .NET 4.5 existiert. Diese Funktion wurde seit C# 7 mit der .NET 4.7-Version mit der ValueTask-Struktur erweitert, die als Rückgabe für asynchrone Funktionen verwendet werden kann.
ValueTask-Struktur
Die ValueTask-Struktur erschien erstmals 2015 im corefxlab-Repository. Dieses Repository wird genutzt, um zu experimentieren und neue Ideen zu erforschen, die möglicherweise oder vielleicht auch nicht in das Haupt-CoreFX-Repository gelangen. Das Corefx-Repository ist das Repository, in dem sich alle .NET Core-Basisbibliotheken befinden. Sie wurde von Stephen Taub für die System.Threading.Tasks.Channels-Bibliothek entwickelt und vorgeschlagen. Damals gab Stephan eine kurze Erklärung:
COREFXLab-Bibliotheksadresse:Der Hyperlink-Login ist sichtbar.
Ein ValueTask ist eine eigenständige Vereinigung von T und Task, die es ReadAsync ermöglicht, frei zuzuweisen, um seine verfügbaren T-Werte synchron zurückzugeben (im Gegensatz zur Verwendung von Task.FromResult, das eine Zuweisung einer Task-Instanz erfordert). ValueTask ist abwartbar, sodass der Verbrauch der meisten Instanzen nicht von dem Verbrauch von Aufgaben zu unterscheiden ist. Viele sehen die Vorteile dieser Struktur, die in C# 7 als Teil des System.Threading.Tasks.Extensions NuGet-Pakets enthalten ist. Bevor wir also in die ValueTask-Struktur eintauchen, wollen wir das Problem betrachten, das sie zur Lösung verwendet. Da Task(Task) ein Referenztyp ist, beginnen Sie mitDie asynchrone Methode, die das Task-Objekt zurückgibt, bedeutet, dass es jedes Mal auf dem Heap zugewiesen wird。 Dies ist in vielen Fällen notwendig.
In einigen Fällen liefern asynchrone Methoden jedoch sofort Ergebnisse oder schließen synchron ab. In diesen Fällen ist diese Zuteilung unnötig und kann in leistungskritischen Teilen des Codes teuer werden. Bis zur .NET 4.7-Version gab es keine Möglichkeit, dies zu vermeiden, da asynchrone Methoden Task, Task <T>oder Void zurückgeben mussten (letzteres war meist unerwünscht). In dieser .NET-Version ist dies erweitert, was bedeutet, dass eine asynchrone Methode jeden Typ zurückgeben kann, solange sie eine zugängliche GetAwaiter-Methode hat. ValueTask ist ein konkretes Beispiel dieses Typs und wurde ebenfalls in diese Version aufgenommen.
Sie können das CoreFX-Repository durchsuchen und die vollständige Implementierung von ValueTask sehen, hier ist der API-Abschnitt, an dem wir interessiert sind:
Als Struktur ermöglicht ValueTask das Schreiben asynchroner Methoden, die während der synchronen Laufzeit keinen Speicher zuweisen. Die API-Konsistenz des Async/Await-Konzepts wird auf diese Weise nicht beeinträchtigt. Darüber hinaus wartet diese Struktur auf sich allein, was die Benutzerfreundlichkeit erleichtert. Zum Beispiel, wenn wir diesen einfachen Code ausführen:
In der MultiplyAsync-Methode simulieren wir eine Situation, in der wir Task vermeiden und nur eine einfache ganze Zahl zurückgeben wollen. Dies geschieht in der if-Anweisung der Methode, wo wir im Grunde prüfen, ob der übergebene Parameter null ist. Das Problem istSelbst wenn unsere Bedingung in der if-Anweisung wahr ist, erzeugt der obige Code ein Task-Objekt。 Wir lösen dieses Problem folgendermaßen:
ValueTask und Task
Wie bereits erwähnt, gibt es zwei Hauptvorteile bei der Nutzung von ValueTask:
- Leistungsverbesserungen
- Erhöhung der Implementierungsflexibilität
Was sind also die Zahlen hinter den Leistungsverbesserungen? Beachten Sie diesen Code:
Wenn wir diesen Code ausführen, benötigt man 120 ns, um den JIT auszuführen. Wenn wir nun Task durch ValueTask so ersetzen:
Mit JIT erreichen wir eine Ausführungszeit von 65 ns. Es stimmt, dass wir aufgrund von Task.Delay nicht synchron ausführen, aber wir sehen eine Verbesserung der Ausführungszeit.
Ein weiterer Vorteil, den wir erwähnt haben, ist die erhöhte Flexibilität bei der Umsetzung. Was genau bedeutet das? Nun, Implementierungen asynchroner Schnittstellen, die synchronisiert sein sollten, müssen Task.Run oder Task.FromResult verwenden. Natürlich führt das zu den Leistungsproblemen, die wir zuvor besprochen haben. Wenn wir ValueTask verwenden, wählen wir eher zwischen synchronen oder asynchronen Implementierungen. Beachte, dass dies ein Zeichen dafür sein könnte, dass dein Code nicht gut gestaltet ist, falls dir das passiert.
Beobachten Sie zum Beispiel diese Schnittstelle:
Angenommen, du möchtest es aus dem Code so nennen:
Da wir ValueTask in der Schnittstelle verwenden, kann die Implementierung der Schnittstelle synchron oder asynchron sein. Wir können diesen Vorteil nutzen, indem wir im Grunde einige Funktionen zu IThing überspringen, die das Synchronisationsverhalten übernehmen. Auf diese Weise ist es viel einfacher, diese Oberfläche zu nutzen. Hier ist eine synchrone Implementierung der obigen Schnittstelle:
Hier ist eine asynchrone Implementierung derselben Schnittstelle:
Wir müssen jedoch einige Abwägungen bedenken, bevor wir ValueTask verwenden. Es ist leicht zu denken, dass ValueTask standardmäßig statt Task verwendet werden sollte, was definitiv nicht der Fall ist. Zum Beispiel hilft uns ValueTask zwar dabei, unnötige Zuweisungen bei verfügbarer Ergebnissynchronisation zu vermeiden, enthält aber auch zwei Felder.
Es ist wichtig zu bedenken, dass dies die Struktur ist, die wir hier verwenden, was bedeutet, dass wir Werttypen und all ihre Belastungen verwenden. Aufgabe hingegen ist ein Referenztyp mit nur einem Feld.Wenn Sie ValueTask verwenden, haben wir mehr Daten zum Verarbeiten und Verarbeiten. Wenn eine solche Methode in einer asynchronen Methode abgewartet wird, dann gilt die asynchrone MethodeDie Zustandsmaschine wird ebenfalls größer sein, weil die Speicherung der gesamten Struktur in der Regel mehr Platz erfordert als die Speicherung einer einzelnen Referenz.
Deshalb empfehlen die Leute bei Microsoft tatsächlich, Task oder Task als Standard-Rückgabetyp für asynchrone Methoden zu verwenden. Erst nach der Leistungsanalyse sollten Sie in Erwägung ziehen, zu ValueTask zu wechseln.
Zusammenfassung
ValueTask ist eine Struktur, die in .NET 4.7 eingeführt wurde und uns viele Möglichkeiten bietet, asynchrone Methoden in .NET zu verwenden. Allerdings ist es nicht ohne Preis. Dies ist nützlich für leistungskritische Methoden, die synchron ausgeführt werden. Mit ihnen können wir vermeiden, unnötige Objekte zuzuweisen. Dennoch bringt es als Werttyp alle Probleme mit sich, die Werttypen normalerweise haben. Daher können wir von dieser Struktur profitieren, müssen aber vorsichtig sein.
Ursprüngliche Adresse:Der Hyperlink-Login ist sichtbar.
|
Vorhergehend:【Practical Action】Verwenden Sie Docker, um einen IPsec VPN-Server zu bauenNächster:Um Routing-Table-Locks zu vermeiden, verwenden Sie OpenConnect statt Cisco AnyConnect, um Routing-Tabellensperren zu vermeiden
|