Utwórz ukończone zadanie

197

Chcę utworzyć ukończony Task(nie Task<T>). Czy jest coś wbudowanego w .NET, aby to zrobić?

Powiązane pytanie: Utwórz ukończone zadanie <T>

Tarcze Tymoteusza
źródło
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.Jak myślisz, jakie to problemy? Jeśli umieścisz w pamięci podręcznej jeden, Taskcały program zajmie dodatkową pamięć. To nic . Poza tym można stworzyć ukończone zadanie bez robienia tego, po prostu nie byłoby lepiej.
Servy,
10
Och, moje rozczarowanie nie ma nic wspólnego z koniecznością użycia dodatkowej pamięci. Po prostu wartości śmieci w dowolnym miejscu kodu nie są eleganckie.
Timothy Shields,
1
Zauważ, że dziś jest już ValueTaskukończone zadania (tj. Wartości, które już masz, aby kod był zasadniczo synchroniczny), co pozwoli ci zaoszczędzić przydział.
nawfal

Odpowiedzi:

246

Najnowsza wersja NET (v4.6) dodaje tylko, że wbudowany Task.CompletedTask :

Task completedTask = Task.CompletedTask;

Ta właściwość jest implementowana jako singleton bez blokady, więc prawie zawsze będziesz używać tego samego ukończonego zadania.

i3arnon
źródło
1
Właśnie sprawdziłem najnowszą wersję VS 14 CTP i utworzyłem projekt 4.5.3, Task.CompletedTaskwciąż jest wewnętrzny.
Peter Ritchie
1
2. Albo nie pobrałeś najnowszego CTP (który jest 4 i jest połączony z tą witryną), albo nie podałeś wersji 4.5.3. Oto, co jest na mojej maszynie . @PeterRitchie
i3arnon
Utworzyłem maszynę wirtualną z obrazu Visual Studio 14 na platformie Azure.
Peter Ritchie
1
@PeterRitchie Z szablonu Azure VS14CTP4
i3arnon
Pracuję na Mac OS X z mono 5.4.1.7 i pojawia się ten sam błąd. Jak mogę to naprawić?
Khaled Annajar
152

Task<T>jest domyślnie konwertowalny na Task, więc po prostu wypełnij go Task<T>(dowolną Tdowolną wartością) i użyj tego. Możesz użyć czegoś takiego, aby ukryć fakt, że gdzieś istnieje rzeczywisty wynik.

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

Pamiętaj, że ponieważ nie ujawniamy wyniku, a zadanie jest zawsze zakończone, możemy buforować pojedyncze zadanie i ponownie je wykorzystać.

Jeśli używasz .NET 4.0, a nie masz go FromResult, możesz utworzyć własny, używając TaskCompletionSource:

public static Task<T> FromResult<T>(T value)
{
    var tcs = new TaskCompletionSource<T>();
    tcs.SetResult(value);
    return tcs.Task;
}
Servy
źródło
8
Jeśli używasz wersji 4.0, możesz dodać bibliotekę Microsoft.Bcl.Async, aby uzyskać TaskEx.FromResult, a także inne przydatne rzeczy, takie jak WhenAll
Carl
@Servy Co to zmienia od FromResult (false) i FromResult (true)?
Francesco Bonizzi
3
@FrancescoB. Jeśli kiedykolwiek patrzysz na wynik, to zmienia wartość boolowską wyniku. Jeśli ignorujesz wynik, to nie ma znaczenia.
Servy
66

Moją preferowaną metodą jest wywołanie Task.WhenAll()bez argumentów. Dokumentacja MSDN stwierdza, że ​​„Jeśli dostarczona tablica / wyliczenie nie zawiera żadnych zadań, zwrócone zadanie natychmiast przejdzie w stan RanToCompletion, zanim zostanie zwrócone do programu wywołującego.”. To brzmi jak chcesz.

Aktualizacja: Znalazłem źródło w Microsoft Reference Source ; tam widać, że Task.WhenAll zawiera następujące elementy:

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

Więc Task.CompletedTask jest rzeczywiście wewnętrzny, ale jest ujawniany przez wywołanie WhenAll () bez argumentów.

Richiban
źródło
9
chociaż to jest trochę hacky, ma wsparcie od doktorów, więc nie wiem, trochę to lubię!
sara
2
Dla mnie i mojego ograniczonego kodu .NET 4.5.2 jest wystarczająco elegancki. Dzięki!
Staś Iwanow
36

Chciałbym użyć Task.Delay(0). Wewnętrznie zwraca buforowaną instancję ukończonej Task<T>. I tak właśnie sugeruje obecna odpowiedź, tylko teraz nie musisz buforować instancji samodzielnie, ani nie masz żadnych nieeleganckich wartości śmieci w kodzie.

Może być myślenie można używać Task.Yield()zamiast, ale okazuje się, że rezultatem Task.Yield()jest nie podtypem Task, natomiast wynik Task.Delay(0)jest. To jedna z subtelnych różnic między nimi.

gzak
źródło
30

Możesz użyć Task.FromResult (w .NET 4.5), aby zwrócić ukończone Task<T>.

Jeśli potrzebujesz nie-ogólnego Task, zawsze możesz użyć Task.FromResult(0)lub podobnego, ponieważ Task<T>jest to podklasa Task.

Reed Copsey
źródło
11

Do użytku w .Net 4.6 i nowszych

return Task.CompletedTask;

W przypadku niższej wersji możesz użyć

return new Task(() => { });
Icen
źródło
3
W przypadku podejścia sprzed wersji 4.6 ostrożnie! Musisz rozpocząć zadanie, bo nigdy się nie zakończy! A może return Task.Delay(0);zamiast tego użyć ?
Miquel,
0

Co powiesz na:

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

Możesz pominąć tłumienie ostrzeżeń, jeśli nie masz nic przeciwko.

Dorus
źródło
Uważam, że zaznaczenie metody asyncnadal tworzy cały aparat automatu stanów, nawet jeśli jej nie używasz, więc zwrócenie pustego zadania jest bardziej wydajne w scenariuszach o niskim zużyciu zasobów.
Calvin Fisher,