2016-10-29 4 views
0

Wenn wir ein TaskCompletionSource<T> basierend Task erstellen möchten, das Result hat, müssen wir noch eine T bereitstellen und einen Dummy-Wert festlegen. Wie folgt aus:Welches generische Argument für TaskCompletionSource, wenn kein Ergebnis benötigt wird

Task SomethingAsync() { 
var tcs = new TaskCompletionSource<?>(); 
tcs.SetResult(default(?)); //Can happen later as well, this is for demo purposes. 
return tcs.Task; 
} 

Was ist die beste Art für T aus Performance-Gesichtspunkten zu benutzen?

Es scheint schwierig zu sein, diese Frage nur aus einem Mikro-Benchmark zu beantworten. Ich kann mir vorstellen, dass die Antwort vom Rest der Anwendung abhängt. Wenn wir zum Beispiel TaskCompletionSource<bool> verwenden, führt dies dazu, dass der JIT spezialisierten Code generiert und Speicherverbrauch verursacht. Es wird jedoch keine Speichernutzung hinzugefügt, wenn die Anwendung bereits boolesche Tasks verwendet. Wenn wir TaskCompletionSource<object> verwenden, verwenden wir möglicherweise mehr Speicher für jede Aufgabe (oder nicht abhängig von der Laufzeit).

Deshalb denke ich, dass ein Benchmark allein die Frage nicht beantworten kann und es auch durch Argumentation beantwortet werden muss.

Antwort

3

Wenn Sie wirklich die bestmögliche Lösung wünschen, müssen Sie eine leere Struktur deklarieren. Auf diese Weise muss das System keinen Speicherplatz für die Nutzlast reservieren. Das ist eigentlich das, was in der Basisklassenbibliothek durchgeführt wird:

https://referencesource.microsoft.com/#System.Core/System/Threading/Tasks/TaskExtensions.cs,6e36a68760fb02e6,references

private struct VoidResult { } 

Von dort Sie TaskCompletionSource<VoidResult> verwenden können, und TrySetResult(default(VoidResult)) die Aufgabe als abgeschlossen zu markieren.

Das heißt, es speichert ein kleines bisschen Speicher, aber ich denke nicht, dass es einen Einfluss auf die Ausführungszeit (sogar auf Nanosekunden) hat. Ob Sie ein TaskCompletionSource<byte> (ein für die Payload reserviertes Byte) oder TaskCompletionSource<object> (vier Bytes für die Nutzlast reserviert) verwenden, ist eine 32-Bit-CPU immer noch in der Lage, die Zuweisung in einer einzigen Operation auszuführen.

+0

Gute Idee. Ich frage mich, ob byte, bool und VoidResult aufgrund der Art und Weise, wie das Objektlayout gemacht wird, irgendwelche Bytes speichern. Objekte sind 8 Byte auf 64 Bit ausgerichtet (glaube ich). Außerdem erzwingt dies für jede Assembly, die dies tut, einen neuen Satz von Jitted-Code. – boot4life

+0

@ boot4life Schwer zu beantworten. 'TaskCompletionSource ' hat ein einzelnes Feld (speichert eine 'Task ') und füllt 4 Byte RAM auf x86. Jede zusätzliche Nutzlast würde den reservierten Speicherplatz auf die nächste 4-Byte-Ausrichtung verschieben. Aber ich weiß nicht wirklich, wie 0 Bytes Struktur wie 'VoidResult' behandelt werden. Wenn es richtig gemacht wird (es ist wahrscheinlich), dann bedeutet das, dass das System die TaskCompletionSource in einem einzigen 4-Byte-Bereich (ohne die CLR-spezifischen Header) speichern kann, während TaskCompletionSource und TaskCompletionSource 8 verwenden würden Bytes (wegen der Ausrichtung) –

+1

Die TaskCompletionSource wäre in keinem Fall betroffen, aber die Task Größe wäre. In 4.6 sehe ich ein einzelnes 'T result'-Feld in der abgeleiteten Klasse. Idealerweise würde der abgeleitete Teil am Ende layouted leer enden. – boot4life

Verwandte Themen