Im folgenden Code LazyBar
muss die Klasse aufgrund der Schnittstelle eine Aufgabe von ihrer Methode zurückgeben (und kann aus Gründen der Argumentation nicht geändert werden). Wenn die LazyBar
Implementierung insofern ungewöhnlich ist, als sie schnell und synchron ausgeführt wird, wie kann eine No-Operation-Task am besten von der Methode zurückgegeben werden?
Ich habe mit gegangen Task.Delay(0)
unten, aber ich würde gerne wissen, ob dies irgendwelche Performance Nebenwirkungen hat , wenn die Funktion aufgerufen wird , eine Menge (für Argumente willen, sagen mehrere hundert Mal pro Sekunde):
- Entspannt sich dieser syntaktische Zucker zu etwas Großem?
- Verstopft es den Thread-Pool meiner Anwendung?
- Reicht das Compiler-Cleaver aus, um
Delay(0)
anders damit umzugehen ? - Wäre
return Task.Run(() => { });
anders?
Gibt es einen besseren Weg?
using System.Threading.Tasks;
namespace MyAsyncTest
{
internal interface IFooFace
{
Task WillBeLongRunningAsyncInTheMajorityOfImplementations();
}
/// <summary>
/// An implementation, that unlike most cases, will not have a long-running
/// operation in 'WillBeLongRunningAsyncInTheMajorityOfImplementations'
/// </summary>
internal class LazyBar : IFooFace
{
#region IFooFace Members
public Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
{
// First, do something really quick
var x = 1;
// Can't return 'null' here! Does 'Task.Delay(0)' have any performance considerations?
// Is it a real no-op, or if I call this a lot, will it adversely affect the
// underlying thread-pool? Better way?
return Task.Delay(0);
// Any different?
// return Task.Run(() => { });
// If my task returned something, I would do:
// return Task.FromResult<int>(12345);
}
#endregion
}
internal class Program
{
private static void Main(string[] args)
{
Test();
}
private static async void Test()
{
IFooFace foo = FactoryCreate();
await foo.WillBeLongRunningAsyncInTheMajorityOfImplementations();
return;
}
private static IFooFace FactoryCreate()
{
return new LazyBar();
}
}
}
Task.FromResult<object>(null)
.Antworten:
Das Verwenden von
Task.FromResult(0)
oderTask.FromResult<object>(null)
verursacht weniger Overhead als das Erstellen einesTask
Ausdrucks mit einem No-Op-Ausdruck. Beim Erstellen einesTask
mit einem vordefinierten Ergebnis ist kein Planungsaufwand erforderlich.Heute würde ich empfehlen, Task.CompletedTask zu verwenden, um dies zu erreichen.
quelle
return default(YourReturnType);
Task.CompletedTask
Ich bin mir nicht sicher, könnte aber den Trick machen! (erfordert aber .net 4.6)Task.FromResult<TResult>
, was a zurückgibtTask<TResult>
, den Rückgabetyp vonTask
(keine Typparameter) erfüllt?Um Reed Copseys Antwort zur Verwendung zu ergänzen
Task.FromResult
, können Sie die Leistung noch weiter verbessern, wenn Sie die bereits abgeschlossene Aufgabe zwischenspeichern, da alle Instanzen abgeschlossener Aufgaben gleich sind:Mit können
TaskExtensions.CompletedTask
Sie dieselbe Instanz in der gesamten App-Domain verwenden.Die neueste Version von .Net Framework (v4.6) fügt genau das mit der
Task.CompletedTask
statischen Eigenschaft hinzuquelle
public Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
sowiepublic async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
. Also können wir entwederreturn CompletedTask;
oderawait CompletedTask;
. Was ist vorzuziehen (vielleicht effizienter oder kongruenter)?Task.Delay(0)
wie in der akzeptierten Antwort war ein guter Ansatz, da es sich um eine zwischengespeicherte Kopie eines abgeschlossenen handeltTask
.Ab 4.6 gibt es jetzt
Task.CompletedTask
einen expliziteren Zweck, der jedoch nicht nurTask.Delay(0)
immer noch eine einzelne zwischengespeicherte Instanz zurückgibt , sondern auch dieselbe einzelne zwischengespeicherte Instanz wieTask.CompletedTask
.Die zwischengespeicherte Natur von beidem bleibt garantiert konstant, aber als implementierungsabhängige Optimierungen, die nur implementierungsabhängig sind, als Optimierungen (das heißt, sie würden immer noch korrekt funktionieren, wenn sich die Implementierung in etwas ändert, das noch gültig ist),
Task.Delay(0)
wurde verwendet besser als die akzeptierte Antwort.quelle
Task.CompletedTask
kann aber nicht in PCL-Projekten verwendet werden, selbst wenn ich die .net-Version auf 4.6 (Profil 7) eingestellt habe, die gerade in VS2017 getestet wurde.Task.CompletedTask => Task.Delay(0);
, um das zu unterstützen, also ziehe ich an Ich weiß es nicht genau.Vor kurzem ist dies aufgetreten und es wurden immer wieder Warnungen / Fehler bezüglich der ungültigen Methode angezeigt.
Wir haben es uns zur Aufgabe gemacht, den Compiler zu beruhigen, und das klärt es auf:
Dies vereint die bisher besten Ratschläge hier. Es ist keine return-Anweisung erforderlich, es sei denn, Sie tun tatsächlich etwas in der Methode.
quelle
public Task MyVoidAsyncMethod() {}
ist völlig das gleiche wie oben Methode. Wenn es einen Anwendungsfall für diese Verwendung gibt, fügen Sie bitte den zusätzlichen Code hinzu.quelle
Wenn Sie den angegebenen Typ zurückgeben müssen:
quelle
Ich bevorzuge die
Task completedTask = Task.CompletedTask;
Lösung von .Net 4.6, aber ein anderer Ansatz besteht darin, die Methode als asynchron zu markieren und void zurückzugeben:Sie erhalten eine Warnung (CS1998 - Asynchrone Funktion ohne Ausdruck warten), die Sie in diesem Zusammenhang jedoch ignorieren können.
quelle
Wenn Sie Generika verwenden, geben uns alle Antworten einen Kompilierungsfehler. Sie können verwenden
return default(T);
. Beispiel unten zur weiteren Erläuterung.quelle
quelle