¿Cuál es el uso de Task.FromResult <TResult> en C #

189

En C # y TPL ( Task Parallel Library ), la Taskclase representa un trabajo en curso que produce un valor de tipo T.

Me gustaría saber cuál es la necesidad del método Task.FromResult .

Es decir: en un escenario en el que ya tiene el valor producido a mano, ¿cuál es la necesidad de volver a incluirlo en una tarea?

Lo único que viene a la mente es que se usa como un adaptador para otros métodos que aceptan una instancia de Task.

ácido lisérgico
fuente
30
Hasta cierto punto estoy de acuerdo con eso, pero la creación de páginas densas, útiles, consolidadas y orientadas a la discusión como esta es un gran beneficio. Casi siempre aprendo más de una buena y densa página de stackoverflow que de buscar en Google e investigar en varios lugares, por lo que en este caso, estoy muy contento de que haya publicado esto.
Alex Edelstein
42
Creo que Google me lleva a SO y SO me pide que vaya a Google. Es una referencia circular :)
usuario de gmail

Respuestas:

258

Hay dos casos de uso comunes que he encontrado:

  1. Cuando implementa una interfaz que permite llamadas asincrónicas, pero su implementación es sincrónica.
  2. Cuando está troquelando / burlándose de código asincrónico para realizar pruebas.
Stephen Cleary
fuente
66
Un buen caso para el # 1 es un servicio web. Podría tener un método de servicio sincrónico que regrese Task.FromResulty un cliente que aguarde asincrónicamente la E / S de red. De esta manera, puede compartir la misma interfaz entre cliente / servidor ChannelFactory.
Nelson Rothermel
2
Por ejemplo, el método ChallengeAsync. ¿Qué pensaban los diseñadores de MS? No hay absolutamente ninguna razón para que este método devuelva una tarea. Y todo el código de muestra de MS simplemente tiene FromResult (0). ¡Esperemos que el compilador sea lo suficientemente inteligente como para optimizar esto, y en realidad no genera un nuevo hilo y luego lo mata de inmediato!
John Henckel
14
@ JohnHenckel: OWIN está diseñado desde cero para ser asíncrono. Las interfaces y las clases base a menudo usan firmas asíncronas porque solo permite (no obliga ) que la implementación sea asíncrona. Por lo tanto, es similar a IEnumerable<T>derivar de IDisposable: permite que lo enumerable tenga recursos disponibles, no lo obliga a hacerlo. Ni FromResult, asyncni awaitgenerarán hilos.
Stephen Cleary
44
@StephenCleary hmhm, gracias por explicar eso. Asumí que la espera engendraría, pero lo intenté y veo que no. Solo Task.Run lo hace. Por lo tanto, x = espera Task.FromResult (0); es equivalente a decir x = 0; eso es confuso, ¡pero es bueno saberlo!
John Henckel
44
@OlegI: Para las operaciones de E / S, la mejor solución es implementarlo de forma asincrónica, pero a veces no tienes esa opción. Además, a veces puede implementarlo sincrónicamente (p. Ej., Un resultado almacenado en caché, recurriendo a una implementación asincrónica si el valor no está almacenado en caché). De manera más general, un Taskmétodo de retorno significa " puede ser asíncrono". Entonces, a veces los métodos reciben una firma asincrónica sabiendo muy bien que algunas implementaciones serán síncronas (por ejemplo, NetworkStreamdeberían ser asíncronas, pero MemoryStreamdeberían estar sincronizadas).
Stephen Cleary el
50

Un ejemplo sería un método que utiliza un caché. Si el resultado ya está calculado, puede devolver una tarea completada con el valor (usando Task.FromResult). Si no es así, continúa y devuelve una tarea que representa el trabajo en curso.

Ejemplo de caché: ejemplo de caché usando Task.FromResult para valores calculados previamente

Matt Smith
fuente
Y las tareas completadas, como las que se devuelven Task.FromResult, se pueden almacenar en caché.
Paulo Morgado
1
@Paulo: mantener todo el objeto Task en la memoria parece mucho más derrochador que almacenar en caché solo el resultado.
Ben Voigt
2
Esperar "tareas de valor" ya están en caché. No recuerdo exactamente cuáles, pero creo que Task.FromResult(0), Task.FromResult(1), Task.FromResult(false)y Task.FromResult(true)se almacenan en caché. Se supone que no debe almacenar en caché una tarea para acceder a la red, pero una del resultado está perfectamente bien. ¿Preferiría crear uno cada vez que necesite devolver el valor?
Paulo Morgado
44
... y para responder a mi propia pregunta, el beneficio de un caché de Tareas es que algunas de ellas pueden completarse tareas, y otras pueden ser tareas que aún no han terminado. Las personas que llaman no tienen que preocuparse: hacen una llamada asincrónica, y si ya está completa, reciben una respuesta inmediatamente cuando esperan, si no, la reciben más tarde. Sin estas tareas almacenadas en caché, (a) requeriría dos mecanismos diferentes, uno sincronizado y otro asíncrono: engorroso para la persona que llama, o (b) tendría que crear dinámicamente una tarea, cada vez que la persona que llama solicita una respuesta que ya está disponible (si solo habíamos almacenado en caché un TResult).
ToolmakerSteve
1
Ahora lo entiendo. La redacción de la respuesta fue un poco confusa. La tarea en sí no tiene un mecanismo de "almacenamiento en caché" integrado. Pero si escribió un mecanismo de almacenamiento en caché para ... digamos ... descargar archivos, Task <File> GetFileAync (), podría devolver instantáneamente un archivo que ya está en caché utilizando Task.FromResult (cachedFile), y esperar funcionaría sincrónicamente, ahorrando tiempo al no tener el interruptor de hilo.
Brain2000
31

Úselo cuando desee crear un método esperado sin usar la palabra clave asíncrona. Encontré este ejemplo:

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

Aquí está creando su propia implementación de la interfaz IHttpActionResult para utilizarla en una acción de API web. Se espera que el método ExecuteAsync sea asíncrono, pero no tiene que usar la palabra clave asíncrona para hacerlo asíncrono y estar a la espera. Como ya tiene el resultado y no necesita esperar nada, es mejor usar Task.FromResult.

Edminsson
fuente
4

Use Task.FromResult cuando desee tener una operación asincrónica, pero a veces el resultado está disponible sincrónicamente. Puede encontrar una buena muestra aquí http://msdn.microsoft.com/en-us/library/hh228607.aspx .

Alborz
fuente
en su buena muestra, el resultado no está disponible sincrónicamente, la operación es todo asíncrona, Task.FromResultse usa para obtener un resultado asíncrono previamente almacenado en caché.
Rodrigo Reis
1

Yo diría que podría usar Task.FromResult para métodos sincrónicos que tardan mucho tiempo en completarse mientras puede realizar otro trabajo independiente en su código. Sin embargo, prefiero hacer esos métodos para llamar asíncrono. Pero imagine la situación en la que no tiene control sobre el código llamado y desea ese procesamiento paralelo implícito.

Vikingo
fuente