A cosa serve Task.FromResult <TResult> in C #


189

In C # e TPL ( Task Parallel Library ), la Taskclasse rappresenta un lavoro in corso che produce un valore di tipo T.

Vorrei sapere qual è la necessità del metodo Task.FromResult ?

Cioè: in uno scenario in cui hai già il valore prodotto a portata di mano, qual è la necessità di ricollocarlo in un'attività?

L'unica cosa che viene in mente è che viene utilizzato come adattatore per altri metodi che accettano un'istanza Task.



30
In una certa misura sono d'accordo con questo, ma la creazione di pagine dense, utili, consolidate e orientate alla discussione come questa è un enorme vantaggio. Quasi sempre imparo di più da una buona e densa pagina di StackOverflow che dal cercare su Google e fare ricerche in più luoghi, quindi in questo caso, sono davvero felice che abbia pubblicato questo.
Alex Edelstein,

42
Penso che Google mi porti su SO e SO mi chieda di andare su Google. È un riferimento circolare :)
utente di gmail il

Risposte:


258

Sono stati trovati due casi d'uso comuni:

  1. Quando stai implementando un'interfaccia che consente ai chiamanti asincroni, ma l'implementazione è sincrona.
  2. Quando stai testando / deridendo il codice asincrono per il test.

6
Un buon caso per # 1 è un servizio web. Potresti avere un metodo di servizio sincrono che ritorna Task.FromResulte un client che attende in modo asincrono per l'I / O di rete. In questo modo è possibile condividere la stessa interfaccia tra client / server utilizzando ChannelFactory.
Nelson Rothermel,

2
Ad esempio il metodo ChallengeAsync. Cosa pensavano i progettisti di MS? Non c'è assolutamente alcun motivo per questo metodo per restituire un'attività. E tutto il codice di esempio di MS ha semplicemente FromResult (0). Spero che il compilatore sia abbastanza intelligente da ottimizzarlo subito e in realtà non genera un nuovo thread e poi lo uccide subito!
John Henckel,

14
@JohnHenckel: OWIN è progettato da zero per essere asincrono. Le interfacce e le classi di base usano spesso firme asincrone perché consentono (e non forzano ) l'implementazione a essere asincrona. Quindi è simile a IEnumerable<T>derivare da IDisposable- permette all'enumerabile di avere risorse usa e getta, non lo costringe a. Né FromResult, asyncawaitgenererà discussioni.
Stephen Cleary,

4
@StephenCleary hmhm, grazie per averlo spiegato. Avevo supposto che l'attesa sarebbe stata generata, ma l'ho provato e vedo che non lo fa. Fa solo Task.Run. Pertanto, x = await Task.FromResult (0); equivale a dire x = 0; è confuso, ma buono a sapersi!
John Henckel,

4
@OlegI: per le operazioni di I / O, la soluzione migliore è implementarla in modo asincrono, ma a volte non hai quella scelta. Inoltre, a volte è possibile implementarlo in modo sincrono (ad esempio, un risultato memorizzato nella cache, ricadendo in un'implementazione asincrona se il valore non è memorizzato nella cache). Più in generale, un Taskmetodo di ritorno significa " può essere asincrono". Quindi a volte ai metodi viene data una firma asincrona sapendo perfettamente che alcune implementazioni saranno sincrone (ad esempio, NetworkStreamdovrebbero essere asincrone, ma MemoryStreamdovrebbero essere sincronizzate).
Stephen Cleary,

50

Un esempio potrebbe essere un metodo che utilizza una cache. Se il risultato è già stato calcolato, è possibile restituire un'attività completata con il valore (utilizzando Task.FromResult). In caso contrario, si procede e si restituisce un'attività che rappresenta il lavoro in corso.

Esempio di cache: Esempio di cache che utilizza Task.FromResult per i valori pre-calcolati


E le attività completate, come quelle restituite Task.FromResult, possono essere memorizzate nella cache.
Paulo Morgado,

1
@Paulo: mantenere in memoria l'intero oggetto Task sembra molto più dispendioso della memorizzazione nella cache solo del risultato.
Ben Voigt,

2
I "task di valore" previsti sono già memorizzati nella cache. Non ricordo esattamente quali quelli, ma credo che Task.FromResult(0), Task.FromResult(1), Task.FromResult(false)e Task.FromResult(true)vengono memorizzati nella cache. Non è necessario memorizzare nella cache un'attività per un accesso alla rete, ma una risultante va benissimo. Preferisci crearne uno ogni volta che devi restituire il valore?
Paulo Morgado,

4
... e per rispondere alla mia domanda, il vantaggio di una cache di Task è che alcuni di essi possono essere completati e altri possono essere quelli che non sono ancora finiti. I chiamanti non devono preoccuparsi: fanno una chiamata asincrona e, se è già completata, ricevono immediatamente una risposta quando attendono, in caso contrario, la ricevono in seguito. Senza queste attività memorizzate nella cache, sarebbe (a) richiedere due diversi meccanismi, una sincronizzazione e una asincrona - ingombrante per il chiamante, oppure (b) creare dinamicamente un'attività, ogni volta che un chiamante chiede una risposta che è già disponibile (se avevamo solo memorizzato nella cache un TResult).
ToolmakerSteve

1
Ora capisco. La formulazione della risposta era leggermente confusa. L'attività stessa non ha un meccanismo di "memorizzazione nella cache" incorporato. Ma se hai scritto un meccanismo di memorizzazione nella cache per ... diciamo .... download di file, Attività <File> GetFileAync (), potresti restituire istantaneamente un file che è già nella cache usando Task.FromResult (cachedFile) e attendi verrebbe eseguito in modo sincrono, risparmiando tempo senza avere il thread switch.
Brain2000,

31

Usalo quando vuoi creare un metodo attendibile senza usare la parola chiave asincrona. Ho trovato questo esempio:

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);
    }
}

Qui stai creando la tua implementazione dell'interfaccia IHttpActionResult da utilizzare in un'azione Web Api. Il metodo ExecuteAsync dovrebbe essere asincrono ma non è necessario utilizzare la parola chiave asincrona per renderla asincrona e attendibile. Dato che hai già il risultato e non devi aspettare nulla, è meglio usare Task.FromResult.



4

Utilizzare Task.FromResult quando si desidera eseguire un'operazione asincrona, ma a volte il risultato è sincronizzato. Puoi trovare un buon esempio qui http://msdn.microsoft.com/en-us/library/hh228607.aspx .


nel tuo buon esempio, il risultato non è in mano in modo sincrono, l'operazione è tutta asincrona, Task.FromResultviene utilizzata per ottenere un risultato asincrono precedentemente memorizzato nella cache.
Rodrigo Reis,

1

Direi che potresti usare Task.FromResult per metodi sincroni che richiedono molto tempo per il completamento, mentre puoi fare altri lavori indipendenti nel tuo codice. Preferisco comunque fare quei metodi per chiamare asincrono. Ma immagina la situazione in cui non hai alcun controllo sul codice chiamato e desideri quell'elaborazione parallela implicita.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.