Erstellen Sie eine abgeschlossene Aufgabe

197

Ich möchte eine fertige Task(nicht Task<T>) erstellen . Ist in .NET etwas eingebaut, um dies zu tun?

Eine verwandte Frage: Erstellen Sie eine abgeschlossene Aufgabe <T>

Timothy Shields
quelle
2
It seems like the answer I'm getting from everyone is that using a garbage value like this is the correct way. That there isn't a way to do this without the garbage value is disappointing -- oh well.Welche Probleme hat das Ihrer Meinung nach? Wenn Sie einen einzelnen zwischenspeichern, belegt TaskIhr gesamtes Programm ein zusätzliches Stück Speicher. Das ist nichts . Man könnte auch eine erledigte Aufgabe erstellen, ohne das zu tun, es wäre einfach nicht besser.
Servy
10
Oh, meine Enttäuschung hat nichts damit zu tun, dass ich zusätzlichen Speicher verwenden muss. Es ist nur so, dass Müllwerte überall im Code nicht elegant sind.
Timothy Shields
1
Beachten Sie, dass es heute ValueTaskfür abgeschlossene Aufgaben gibt (dh für Werte, die Sie bereits haben, damit der Code im Wesentlichen synchron ist), wodurch Sie eine Zuordnung sparen.
Nawfal

Antworten:

246

Die neueste Version von .Net (v4.6) fügt genau das hinzu, eine integrierte Task.CompletedTask :

Task completedTask = Task.CompletedTask;

Diese Eigenschaft ist als No-Lock-Singleton implementiert, sodass Sie fast immer dieselbe abgeschlossene Aufgabe verwenden würden.

i3arnon
quelle
1
Gerade das neueste VS 14 CTP überprüft und ein 4.5.3-Projekt erstellt, Task.CompletedTaskist noch intern.
Peter Ritchie
1
2. Entweder haben Sie nicht das neueste CTP heruntergeladen (das 4 ist und von dieser Site verlinkt wurde) oder Sie haben Version 4.5.3 nicht angegeben. Hier ist, was auf meiner Maschine ist . @ PeterRitchie
i3arnon
Ich habe eine VM aus dem Visual Studio 14-Image in Azure erstellt.
Peter Ritchie
1
@ PeterRitchie Aus der Azure VS14CTP4-Vorlage
i3arnon
Ich arbeite unter Mac OS X mit Mono 5.4.1.7 und erhalte den gleichen Fehler. Wie kann ich das beheben?
Khaled Annajar
152

Task<T>ist implizit konvertierbar in Task, also holen Sie sich einfach eine vollständige Task<T>(mit einem Tbeliebigen Wert) und verwenden Sie diese. Sie können so etwas verwenden, um die Tatsache zu verbergen, dass irgendwo ein tatsächliches Ergebnis vorliegt.

private static Task completedTask = Task.FromResult(false);
public static Task CompletedTask()
{
    return completedTask;
}

Beachten Sie, dass wir eine einzelne Aufgabe zwischenspeichern und wiederverwenden können, da wir das Ergebnis nicht verfügbar machen und die Aufgabe immer abgeschlossen ist.

Wenn Sie .NET 4.0 verwenden und nicht haben FromResult, können Sie Ihre eigenen erstellen mit TaskCompletionSource:

public static Task<T> FromResult<T>(T value)
{
    var tcs = new TaskCompletionSource<T>();
    tcs.SetResult(value);
    return tcs.Task;
}
Servieren
quelle
8
Wenn Sie 4.0 verwenden, können Sie die Microsoft.Bcl.Async-Bibliothek hinzufügen, um TaskEx.FromResult sowie andere nützliche Dinge wie WhenAll
Carl
@Servy Was ändert sich von FromResult (false) und FromResult (true)?
Francesco Bonizzi
3
@FrancescoB. Wenn Sie sich jemals das Ergebnis ansehen, ändert es den booleschen Wert des Ergebnisses. Wenn Sie das Ergebnis ignorieren, ist das Ergebnis irrelevant.
Servy
66

Meine bevorzugte Methode hierfür ist das Aufrufen Task.WhenAll()ohne Argumente. In der MSDN-Dokumentation heißt es: "Wenn das bereitgestellte Array / die Aufzählung keine Aufgaben enthält, wechselt die zurückgegebene Aufgabe sofort in einen RanToCompletion-Status, bevor sie an den Aufrufer zurückgegeben wird." Das klingt nach dem, was du willst.

Update: Ich habe die Quelle bei Microsoft's Reference Source gefunden . Dort können Sie sehen, dass Task.WhenAll Folgendes enthält:

return (tasks.Length == 0) ? // take shortcut if there are no tasks upon which to wait
            Task.CompletedTask :
            new WhenAllPromise(tasks);

Task.CompletedTask ist also zwar intern, wird jedoch durch Aufrufen von WhenAll () ohne Argumente verfügbar gemacht.

Richiban
quelle
9
Obwohl es sich irgendwie hackig anfühlt, hat es Unterstützung von den Dokumenten, also weiß ich nicht, ich mag es irgendwie!
Sara
2
Für mich und meinen eingeschränkten .NET 4.5.2-Code ist es elegant genug. Vielen Dank!
Stas Ivanov
36

Ich würde verwenden Task.Delay(0). Intern wird eine zwischengespeicherte Instanz eines abgeschlossenen Objekts zurückgegeben Task<T>. Dies ist genau das, was die aktuelle Antwort ohnehin vorschlägt. Nur jetzt müssen Sie weder eine Instanz selbst zwischenspeichern, noch haben Sie unelegante Müllwerte in Ihrem Code.

Sie könnten denken , die Sie verwenden können , Task.Yield()statt, aber es stellt sich heraus , das Ergebnis Task.Yield()ist nicht ein Subtyp von Task, während das Ergebnis Task.Delay(0)ist. Das ist einer der subtilen Unterschiede zwischen den beiden.

gzak
quelle
30

Sie können Task.FromResult (in .NET 4.5) verwenden, um ein abgeschlossenes Ergebnis zurückzugeben Task<T>.

Wenn Sie eine nicht generische benötigen Task, können Sie immer Task.FromResult(0)oder ähnlich verwenden, da Task<T>es sich um eine Unterklasse von handelt Task.

Reed Copsey
quelle
11

Für .Net 4.6 und höher verwenden

return Task.CompletedTask;

Für niedrigere Version können Sie verwenden

return new Task(() => { });
Icen
quelle
3
Vorsichtig für den Ansatz vor 4.6! Sie müssen die Aufgabe starten, sonst wird sie nie abgeschlossen! Wie wäre es return Task.Delay(0);stattdessen zu verwenden?
Miquel
0

Wie wäre es mit:

#pragma warning disable 1998
    public async Task emptyTask() {
    }
#pragma warning restore 1998

Sie können die Warnunterdrückung weglassen, wenn Sie nichts dagegen haben.

Dorus
quelle
Ich glaube, dass durch das Markieren einer Methode asyncimmer noch der gesamte Zustandsmaschinenapparat erstellt wird, auch wenn Sie ihn nicht verwenden. Daher ist das Zurückgeben einer leeren Aufgabe für Szenarien mit geringen Ressourcen effizienter.
Calvin Fisher