wake-up-neo.com

Wenn meine Schnittstelle Task zurückgeben muss, was ist der beste Weg für eine Implementierung ohne Operation?

Im folgenden Code muss die Klasse LazyBar aufgrund der Schnittstelle eine Aufgabe von ihrer Methode zurückgeben (und aus Gründen der Argumentation kann sie nicht geändert werden). Wenn die Implementierung von LazyBar 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 bin mit Task.Delay(0) weiter unten gegangen, möchte jedoch wissen, ob dies irgendwelche Leistungsnebenwirkungen hat, wenn die Funktion lot (aus Gründen, die Hunderte von Malen bedeuten) genannt wird eine Sekunde):

  • Entspannt sich dieser syntaktische Zucker zu etwas Großem?
  • Verstopft es den Thread-Pool meiner Anwendung?
  • Reicht der Compiler-Cleaver aus, um mit Delay(0) anders 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();
        }
    }
}
373
Jon Rea

Die Verwendung von Task.FromResult(0) oder Task.FromResult<object>(null) verursacht weniger Aufwand als das Erstellen eines Task mit einem No-Op-Ausdruck. Beim Erstellen eines Task mit einem zuvor festgelegten Ergebnis ist kein Planungsaufwand erforderlich.


Heute würde ich empfehlen, Task.CompletedTask zu verwenden, um dies zu erreichen.

539
Reed Copsey

Hinzufügen zu Reed Copseys Antwort über die Verwendung von Task.FromResult, Sie können die Leistung noch weiter verbessern, wenn Sie die bereits abgeschlossene Aufgabe zwischenspeichern, da alle Instanzen abgeschlossener Aufgaben gleich sind:

public static class TaskExtensions
{
    public static readonly Task CompletedTask = Task.FromResult(false);
}

Mit TaskExtensions.CompletedTask Sie können dieselbe Instanz in der gesamten App-Domäne verwenden.


Die neueste Version von .Net Framework (v4.6) fügt genau das mit Task.CompletedTask statische Eigenschaft

Task completedTask = Task.CompletedTask;
173
i3arnon

Task.Delay(0) wie in der akzeptierten Antwort war ein guter Ansatz, da es sich um eine zwischengespeicherte Kopie eines vollständigen Task handelt.

Ab 4.6 gibt es nun Task.CompletedTask, Was in seinem Zweck genauer ist, aber nicht nur, dass Task.Delay(0) immer noch eine einzelne zwischengespeicherte Instanz zurückgibt, sondern auch die selbe einzelne zwischengespeicherte Instanz Instanz wie Task.CompletedTask.

Die zwischengespeicherte Natur von keinem bleibt garantiert konstant, aber als implementierungsabhängige Optimierungen, die nur als Optimierungen implementierungsabhängig sind (das heißt, sie würden immer noch korrekt funktionieren, wenn die Implementierung auf etwas geändert würde, das noch gültig ist), die Verwendung von Task.Delay(0) war besser als die akzeptierte Antwort.

33
Jon Hanna

Dies ist kürzlich aufgetreten und es werden immer wieder Warnungen/Fehler angezeigt, dass die Methode ungültig ist.

Wir sind im Geschäft, den Compiler zu beschwichtigen, und das klärt es auf:

    public async Task MyVoidAsyncMethod()
    {
        await Task.CompletedTask;
    }

Dies bringt das Beste aller Ratschläge zusammen, die bisher hier gegeben wurden. Eine return-Anweisung ist nur erforderlich, wenn Sie tatsächlich etwas in der Methode tun.

14

Wenn Sie den angegebenen Typ zurückgeben müssen:

Task.FromResult<MyClass>(null);
4
trashmaker_

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:

    public async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
    {
    }

Sie erhalten eine Warnung (CS1998 - Async-Funktion ohne Wartezeitausdruck), die Sie in diesem Kontext jedoch ignorieren können.

3
Remco te Wierik
return Task.CompletedTask; // this will make the compiler happy
3
Xin