Qual é o uso de Task.FromResult <TResult> em C #

189

Em C # e TPL ( Task Parallel Library ), a Taskclasse representa um trabalho contínuo que produz um valor do tipo T.

Gostaria de saber qual é a necessidade do método Task.FromResult ?

Ou seja: em um cenário em que você já possui o valor produzido em mãos, qual é a necessidade de envolvê-lo novamente em uma tarefa?

A única coisa que vem à mente é que ele é usado como algum adaptador para outros métodos que aceitam uma instância de tarefa.

ácido lisérgico
fonte
30
Até certo ponto, concordo com isso, mas a criação de páginas densas, úteis, consolidadas e orientadas para a discussão como essa é um enorme benefício. Eu quase sempre aprendo mais com uma página boa e densa do stackoverflow do que pesquisando e pesquisando em vários lugares. Portanto, nesse caso, fico muito feliz por ele ter postado isso.
precisa
41
Eu acho que o Google me leva a SO e SO me pede para ir ao Google. É uma referência circular :)
gmail user

Respostas:

258

Encontrei dois casos de uso comuns:

  1. Quando você está implementando uma interface que permite chamadores assíncronos, mas sua implementação é síncrona.
  2. Quando você está stubbing / zombando de código assíncrono para teste.
Stephen Cleary
fonte
6
Um bom argumento para o número 1 é um serviço da web. Você pode ter um método de serviço síncrono que retorna Task.FromResulte um cliente que aguarda assincronamente a E / S da rede. Dessa forma, você pode compartilhar a mesma interface entre cliente / servidor usando ChannelFactory.
Nelson Rothermel
2
Por exemplo, o método ChallengeAsync. WTF estavam pensando os designers da MS? Não há absolutamente nenhuma razão para esse método retornar uma tarefa. E todo o código de exemplo da MS simplesmente possui FromResult (0). Esperamos que o compilador seja inteligente o suficiente para otimizar isso, e não crie um novo thread e o mate imediatamente!
John Henckel
14
@JohnHenckel: OWIN foi projetado desde o início para ser compatível com async. As interfaces e as classes base geralmente usam assinaturas assíncronas porque apenas permitem (não força ) a implementação a ser assíncrona. Portanto, é semelhante a IEnumerable<T>derivar de IDisposable- permite que o enumerável tenha recursos disponíveis, não o obriga . Nem FromResult, asyncnem awaitgerará tópicos.
Stephen Cleary
4
@StephenCleary hmhm, obrigado por explicar isso. Eu tinha assumido que a espera iria aparecer, mas eu tentei e vejo que não. Somente o Task.Run faz. Portanto, x = aguarda Task.FromResult (0); é equivalente a dizer x = 0; isso é confuso, mas é bom saber!
John Henckel
4
@ OlegI: Para operações de E / S, a melhor solução é implementá-lo de forma assíncrona, mas às vezes você não tem essa opção. Além disso, às vezes você pode implementá-lo de forma síncrona (por exemplo, um resultado em cache, voltando a uma implementação assíncrona se o valor não estiver em cache). De um modo mais geral, um Taskmétodo de retorno significa " pode ser assíncrono". Então, às vezes, os métodos recebem uma assinatura assíncrona sabendo muito bem que algumas implementações serão síncronas (por exemplo, NetworkStreamdevem ser assíncronas, mas MemoryStreamdevem ser sincronizadas).
Stephen Cleary
50

Um exemplo seria um método que faz uso de um cache. Se o resultado já estiver calculado, você poderá retornar uma tarefa concluída com o valor (usando Task.FromResult). Caso contrário, prossiga e retorne uma tarefa que representa o trabalho em andamento.

Exemplo de cache: exemplo de cache usando Task.FromResult para valores pré-calculados

Matt smith
fonte
E tarefas concluídas, como as retornadas Task.FromResult, podem ser armazenadas em cache.
Paulo Morgado
1
@Paulo: manter todo o objeto Task na memória parece muito mais inútil do que armazenar em cache apenas o resultado.
Ben Voigt
2
Espere que "tarefas de valor" já estejam armazenadas em cache. Não me lembro exatamente qual deles, mas acho que Task.FromResult(0), Task.FromResult(1), Task.FromResult(false)e Task.FromResult(true)são armazenados em cache. Você não deve armazenar em cache uma tarefa para acesso à rede, mas uma do resultado é perfeitamente adequada. Você prefere criar um sempre que precisar retornar o valor?
Paulo Morgado
4
... e para responder à minha própria pergunta, o benefício de um cache de tarefas é que alguns deles podem ser tarefas concluídas e outros podem ser aqueles que ainda não foram concluídos. Os chamadores não precisam se preocupar: eles fazem uma chamada assíncrona e, se já estiver concluída, recebem uma resposta imediatamente quando aguardam; caso contrário, recebem mais tarde. Sem essas tarefas em cache, (a) exigiria dois mecanismos diferentes, um sincronizado e um assíncrono - complicado para o chamador, ou (b) teria que criar dinamicamente uma tarefa, toda vez que um chamador pedir uma resposta que já esteja disponível (se em cache apenas um TResult).
Página
1
Agora eu entendi. A redação da resposta foi um pouco confusa. A própria tarefa não possui um mecanismo de "armazenamento em cache" incorporado. Mas se você escreveu um mecanismo de armazenamento em cache para ... digamos ... baixar arquivos, Task <File> GetFileAync (), você pode retornar instantaneamente um arquivo que já está no cache usando Task.FromResult (cachedFile) e aguardar funcionaria de forma síncrona, economizando tempo ao não ter a chave de linha.
precisa saber é o seguinte
31

Use-o quando desejar criar um método aguardável sem usar a palavra-chave assíncrona. Encontrei este exemplo:

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

Aqui você está criando sua própria implementação da interface IHttpActionResult para ser usada em uma Ação da API da Web. Espera-se que o método ExecuteAsync seja assíncrono, mas você não precisa usar a palavra-chave async para torná-la assíncrona e aguardável. Como você já tem o resultado e não precisa aguardar nada, é melhor usar o Task.FromResult.

Edminsson
fonte
4

Use o Task.FromResult quando desejar ter uma operação assíncrona, mas às vezes o resultado está disponível de forma síncrona. Você pode encontrar uma boa amostra aqui http://msdn.microsoft.com/en-us/library/hh228607.aspx .

Alborz
fonte
na sua boa amostra, o resultado não está em mãos de forma síncrona, a operação é toda assíncrona e Task.FromResulté usada para obter um resultado assíncrono em cache anteriormente.
Rodrigo Reis
1

Eu argumentaria que você poderia usar o Task.FromResult para métodos síncronos que demoram muito tempo para serem concluídos enquanto você pode executar outro trabalho independente em seu código. Eu prefiro criar esses métodos para chamar assíncrono. Mas imagine a situação em que você não tem controle sobre o código chamado e deseja esse processamento paralelo implícito.

Viking
fonte