Quelle est l'utilisation de Task.FromResult <TResult> en C #

189

En C # et TPL ( Task Parallel Library ), la Taskclasse représente un travail en cours qui produit une valeur de type T.

J'aimerais savoir quel est le besoin de la méthode Task.FromResult ?

Autrement dit: dans un scénario où vous avez déjà la valeur produite sous la main, quel est le besoin de la réintégrer dans une tâche?

La seule chose qui me vient à l'esprit est qu'il est utilisé comme adaptateur pour d'autres méthodes acceptant une instance de Task.

acide lysergique
la source
4
est-ce que cela vous aide? msdn.microsoft.com/en-us/library/hh228607.aspx
Izikon
30
Dans une certaine mesure, je suis d'accord avec cela, mais la création de pages denses, utiles, consolidées et axées sur les discussions comme celle-ci est un énorme avantage. J'apprends presque toujours plus d'une bonne page dense de stackoverflow que de googler et de faire des recherches à plusieurs endroits, donc dans ce cas, je suis vraiment content qu'il ait publié ceci.
Alex Edelstein le
42
Je pense que Google m'amène à SO et SO me demande d'aller sur Google. C'est une référence circulaire :)
utilisateur gmail

Réponses:

258

J'ai trouvé deux cas d'utilisation courants:

  1. Lorsque vous implémentez une interface qui autorise les appelants asynchrones, mais que votre implémentation est synchrone.
  2. Lorsque vous stubbing / mocking code asynchrone pour le test.
Stephen Cleary
la source
6
Un bon cas pour le n ° 1 est un service Web. Vous pouvez avoir une méthode de service synchrone qui renvoie Task.FromResultet un client qui attend de manière asynchrone les E / S réseau. De cette façon, vous pouvez partager la même interface entre client / serveur en utilisant ChannelFactory.
Nelson Rothermel
2
Par exemple, la méthode ChallengeAsync. WTF pensaient les concepteurs de MS? Il n'y a absolument aucune raison pour que cette méthode renvoie une tâche. Et tous les exemples de code de MS ont simplement FromResult (0). Espérons que le compilateur soit assez intelligent pour optimiser cela, et ne crée pas réellement un nouveau thread et ne le tue pas tout de suite!
John Henckel
14
@JohnHenckel: OWIN est conçu dès le départ pour être compatible avec l'asynchrone. Les interfaces et les classes de base utilisent souvent des signatures asynchrones car elles permettent simplement (et ne force pas ) l'implémentation à être asynchrone. C'est donc similaire à IEnumerable<T>dériver de IDisposable- cela permet à l'énumérable d'avoir des ressources jetables, pas de le forcer à le faire. Ni FromResult, asyncni awaitne générera de threads.
Stephen Cleary
4
@StephenCleary hmhm, merci d'avoir expliqué cela. J'avais supposé que Wait allait apparaître, mais je l'ai essayé et je vois que ce n'est pas le cas. Seul Task.Run le fait. Par conséquent, x = attendre Task.FromResult (0); équivaut à dire x = 0; c'est déroutant, mais bon à savoir!
John Henckel
4
@OlegI: Pour les opérations d'E / S, la meilleure solution est de l'implémenter de manière asynchrone, mais parfois vous n'avez pas ce choix. De plus, vous pouvez parfois l' implémenter de manière synchrone (par exemple, un résultat mis en cache, revenant à une implémentation asynchrone si la valeur n'est pas mise en cache). Plus généralement, une Taskméthode de retour signifie " peut être asynchrone". Ainsi, parfois, les méthodes reçoivent une signature asynchrone sachant très bien que certaines implémentations seront synchrones (par exemple, NetworkStreamdevraient être asynchrones, mais MemoryStreamdevraient être synchronisées).
Stephen Cleary
50

Un exemple serait une méthode qui utilise un cache. Si le résultat est déjà calculé, vous pouvez renvoyer une tâche terminée avec la valeur (en utilisant Task.FromResult). Si ce n'est pas le cas, continuez et retournez une tâche représentant un travail en cours.

Exemple de cache: exemple de cache utilisant Task.FromResult pour les valeurs pré-calculées

Matt Smith
la source
Et les tâches terminées, telles que celles renvoyées par Task.FromResult, peuvent être mises en cache.
Paulo Morgado du
1
@Paulo: garder l'ensemble de l'objet Task en mémoire semble beaucoup plus inutile que de ne mettre en cache que le résultat.
Ben Voigt
2
Les "tâches de valeur" attendues sont déjà mises en cache. Je ne me souviens pas exactement quels sont ceux, mais je pense Task.FromResult(0), Task.FromResult(1), Task.FromResult(false)et Task.FromResult(true)sont mises en cache. Vous n'êtes pas censé mettre en cache une tâche pour un accès réseau, mais une tâche à partir du résultat convient parfaitement. Préférez-vous en créer un chaque fois que vous devez renvoyer la valeur?
Paulo Morgado
4
... et pour répondre à ma propre question, l'avantage d'un cache de tâches est que certaines d'entre elles peuvent être des tâches terminées, et d'autres peuvent être celles qui ne sont pas encore terminées. Les appelants n'ont pas à s'en soucier: ils font un appel asynchrone, et s'il est déjà terminé, ils obtiennent une réponse immédiatement lorsqu'ils attendent, sinon, ils l'obtiennent plus tard. Sans ces tâches mises en cache, soit (a) nécessiterait deux mécanismes différents, un sync et un async - encombrant pour l'appelant, ou (b) devrait créer dynamiquement une tâche, chaque fois qu'un appelant demande une réponse qui est déjà disponible (si nous n'avions mis en cache qu'un TResult).
ToolmakerSteve
1
Je comprends maintenant. Le libellé de la réponse était légèrement déroutant. La tâche elle-même n'a pas de mécanisme de «mise en cache» intégré. Mais si vous avez écrit un mécanisme de mise en cache pour ... disons .... télécharger des fichiers, Task <File> GetFileAync (), vous pouvez instantanément renvoyer un fichier qui est déjà dans le cache en utilisant Task.FromResult (cachedFile), et l'attente fonctionnerait de manière synchrone, ce qui gagnerait du temps en n'ayant pas le commutateur de thread.
Brain2000
31

Utilisez-le lorsque vous souhaitez créer une méthode en attente sans utiliser le mot clé async. J'ai trouvé cet exemple:

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

Ici, vous créez votre propre implémentation de l'interface IHttpActionResult à utiliser dans une action Web Api. La méthode ExecuteAsync est censée être asynchrone, mais vous n'avez pas besoin d'utiliser le mot clé async pour la rendre asynchrone et attendue. Puisque vous avez déjà le résultat et que vous n'avez rien à attendre, il est préférable d'utiliser Task.FromResult.

Edminsson
la source
4

Utilisez le Task.FromResult lorsque vous souhaitez avoir une opération asynchrone mais que parfois le résultat est en cours de manière synchrone. Vous pouvez trouver un bon exemple ici http://msdn.microsoft.com/en-us/library/hh228607.aspx .

Alborz
la source
dans votre bon exemple, le résultat n'est pas en cours de manière synchrone, l'opération est entièrement asynchrone, le Task.FromResultest utilisé pour obtenir un résultat asynchrone précédemment mis en cache.
Rodrigo Reis
1

Je dirais que vous pouvez utiliser Task.FromResult pour des méthodes synchrones qui prennent beaucoup de temps à se terminer alors que vous pouvez effectuer d'autres travaux indépendants dans votre code. Id plutôt faire ces méthodes pour appeler async. Mais imaginez la situation où vous n'avez aucun contrôle sur le code appelé et que vous voulez ce traitement parallèle implicite.

Viking
la source