Jakie jest zastosowanie dla Task.FromResult <TResult> w C #

189

W języku C # i TPL ( biblioteka zadań równoległych ) Taskklasa reprezentuje trwającą pracę, która generuje wartość typu T.

Chciałbym wiedzieć, jaka jest potrzeba metody Task.FromResult ?

To znaczy: w scenariuszu, w którym masz już pod ręką wytworzoną wartość, jaka jest potrzeba zawinięcia jej z powrotem w Zadanie?

Jedyne, co przychodzi na myśl, to to, że jest używany jako adapter dla innych metod akceptujących instancję Task.

kwas lizergowy
źródło
30
W pewnym stopniu się z tym zgadzam, ale tworzenie takich gęstych, użytecznych, skonsolidowanych, zorientowanych na dyskusję stron jest ogromną korzyścią. Prawie zawsze uczę się więcej z dobrej, gęstej strony z przepływem stosów, niż z googlowania i prowadzenia badań w wielu miejscach, więc w tym przypadku jestem naprawdę zadowolony, że to opublikował.
Alex Edelstein
41
Wydaje mi się, że Google prowadzi mnie do SO i SO proszę, abym poszedł do Google. Jest to cykliczne odniesienie :)
użytkownik

Odpowiedzi:

258

Znalazłem dwa typowe przypadki użycia:

  1. Podczas implementowania interfejsu, który pozwala na wywoływanie asynchroniczne, ale implementacja jest synchroniczna.
  2. Podczas krojenia / kpowania kodu asynchronicznego do testowania.
Stephen Cleary
źródło
6
Dobrym przykładem na nr 1 jest usługa internetowa. Możesz mieć metodę usługi synchronicznej, która zwraca, Task.FromResulti klienta, który czeka asynchronicznie na sieciowe operacje we / wy. W ten sposób możesz współdzielić ten sam interfejs między klientem / serwerem ChannelFactory.
Nelson Rothermel
2
Na przykład metoda ChallengeAsync. WTF czy projektanci w MS myśleli? Nie ma absolutnie żadnego powodu, aby ta metoda zwróciła zadanie. A cały przykładowy kod z MS ma po prostu FromResult (0). Mam nadzieję, że kompilator jest wystarczająco inteligentny, aby to zoptymalizować, i tak naprawdę nie odradza nowego wątku, a następnie nie zabija go od razu!
John Henckel
14
@JohnHenckel: OWIN został zaprojektowany od podstaw, aby był przyjazny dla asynchronizacji. Interfejsy i klasy podstawowe często używają sygnatur asynchronicznych, ponieważ po prostu pozwalają (nie wymuszają ) asynchronizację implementacji. Jest więc podobny do IEnumerable<T>czerpania z IDisposable- pozwala enumerable na dysponowanie zasobami jednorazowymi, a nie zmuszanie go do tego. Ani FromResult, asyncani nie awaitbędzie tarło wątki.
Stephen Cleary
4
@StephenCleary hmhm, dzięki za wyjaśnienie. Zakładałem, że oczekiwanie się odrodzi, ale spróbowałem i widzę, że nie. Tylko Task.Run działa. Dlatego x = czekaj na Task.FromResult (0); jest równoważne z wypowiedzeniem x = 0; to mylące, ale dobrze wiedzieć!
John Henckel
4
@OlegI: W przypadku operacji we / wy najlepszym rozwiązaniem jest wdrożenie asynchroniczne, ale czasami nie masz takiego wyboru. Czasami można również zaimplementować go synchronicznie (np. Wynik w pamięci podręcznej, powrót do implementacji asynchronicznej, jeśli wartość nie jest buforowana). Mówiąc bardziej ogólnie, Taskmetoda powrotu oznacza „ może być asynchroniczna”. Dlatego czasami metody otrzymują sygnaturę asynchroniczną, wiedząc doskonale, że niektóre implementacje będą synchroniczne (np. NetworkStreamPowinny być asynchroniczne, ale MemoryStreampowinny być synchronizowane).
Stephen Cleary
50

Jednym z przykładów może być metoda wykorzystująca pamięć podręczną. Jeśli wynik jest już obliczony, możesz zwrócić ukończone zadanie z wartością (używając Task.FromResult). Jeśli nie, to idź dalej i zwróć zadanie reprezentujące bieżącą pracę.

Przykład pamięci podręcznej: Przykład pamięci podręcznej przy użyciu Task.FromResult dla wstępnie obliczonych wartości

Matt Smith
źródło
Ukończone zadania, takie jak te zwrócone Task.FromResult, mogą być buforowane.
Paulo Morgado
1
@Paulo: przechowywanie całego obiektu Task w pamięci wydaje się o wiele bardziej marnotrawstwem niż buforowanie samego wyniku.
Ben Voigt
2
Oczekuj, że „zadania wartości” są już buforowane. Nie pamiętam dokładnie, które z nich, ale myślę Task.FromResult(0), Task.FromResult(1), Task.FromResult(false)i Task.FromResult(true)są buforowane. Nie należy buforować zadania dostępu do sieci, ale jedno z wyników jest całkowicie w porządku. Czy wolisz utworzyć taki za każdym razem, gdy musisz zwrócić wartość?
Paulo Morgado,
4
... i aby odpowiedzieć na moje pytanie, zaletą pamięci podręcznej Zadań jest to, że niektóre z nich można wykonać zadania, a inne mogą być tymi, które jeszcze się nie zakończyły. Dzwoniący nie muszą się tym przejmować: wykonują połączenie asynchroniczne, a jeśli jest już zakończone, natychmiast otrzymują odpowiedź, gdy czekają, a jeśli nie, otrzymują ją później. Bez tych buforowanych zadań albo (a) wymagałby dwóch różnych mechanizmów, jednego synchronizacji i jednego asynchronicznego - uciążliwego dla dzwoniącego, lub (b) musiałby dynamicznie tworzyć Zadanie, za każdym razem, gdy dzwoniący prosi o odpowiedź, która jest już dostępna (jeśli tylko buforowaliśmy TResult).
ToolmakerSteve
1
Teraz rozumiem. Sformułowanie odpowiedzi było nieco mylące. Samo zadanie nie ma wbudowanego mechanizmu „buforowania”. Ale jeśli napisałeś mechanizm buforowania dla ... powiedzmy ... pobierania plików, Zadanie <File> GetFileAync (), możesz natychmiast zwrócić plik, który jest już w pamięci podręcznej, używając Task.FromResult (cachedFile), i oczekuj działałby synchronicznie, oszczędzając czas, nie mając przełącznika wątku.
Brain2000
31

Użyj go, jeśli chcesz utworzyć oczekiwaną metodę bez użycia słowa kluczowego async. Znalazłem ten przykład:

public class TextResult : IHttpActionResult
{
    string _value;
    HttpRequestMessage _request;

    public TextResult(string value, HttpRequestMessage request)
    {
        _value = value;
        _request = request;
    }
    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var response = new HttpResponseMessage()
        {
            Content = new StringContent(_value),
            RequestMessage = _request
        };
        return Task.FromResult(response);
    }
}

Tutaj tworzysz własną implementację interfejsu IHttpActionResult do użycia w akcji Web Api. Metoda ExecuteAsync powinna być asynchroniczna, ale nie trzeba używać słowa kluczowego async, aby uczynić ją asynchroniczną i wymaganą. Ponieważ masz już wynik i nie musisz na nic czekać, lepiej użyć Task.FromResult.

Edminsson
źródło
4

Użyj Task.FromResult, jeśli chcesz wykonać operację asynchroniczną, ale czasami wynik jest synchroniczny. Dobrą próbkę można znaleźć tutaj http://msdn.microsoft.com/en-us/library/hh228607.aspx .

Alborz
źródło
w dobrej próbce wynik nie jest synchroniczny, operacja jest asynchroniczna, Task.FromResultsłuży do uzyskania wcześniej zapisanego wyniku asynchronicznego w pamięci podręcznej.
Rodrigo Reis
1

Argumentowałbym, że możesz użyć Task.FromResult do metod synchronicznych, których wykonanie zajmuje dużo czasu, a Ty możesz wykonać inną niezależną pracę w swoim kodzie. Zamiast tego raczej używam tych metod do wywoływania asynchronizacji. Ale wyobraź sobie sytuację, w której nie masz kontroli nad wywoływanym kodem i potrzebujesz tego niejawnego przetwarzania równoległego.

Wiking
źródło