web-dev-qa-db-fra.com

Task.FromResult () vs. Task.Run ()

J'ai rencontré pas mal de situations récemment où les méthodes async s'exécutent de manière synchrone, mais retournent quand même une tâche, afin qu'elles puissent être attendues, par exemple.

public virtual Task CreateAsync(TUser user)
{
    ThrowIfDisposed();
    if (user == null) throw new ArgumentNullException("user");
    Context.Save(user);
    Context.Flush();
    return Task.FromResult(0);
}

Il est sûrement préférable de répartir l'opération probablement longue sur un thread et de retourner la tâche toujours active, pour être vraiment attendue:

public virtual Task CreateAsync(TUser user)
{
    ThrowIfDisposed();
    if (user == null) throw new ArgumentNullException("user");
    return Task.Run(() =>
    {
        Context.Save(user);
        Context.Flush();
    });
}

Je soupçonne cependant que le simple fait de filer des fils TPL n'est pas la pratique la plus sûre. Un commentaire sur ces deux modèles différents?

22
ProfK

Si votre méthode est synchrone, vous ne devez pas retourner un Task pour commencer. Créez simplement une méthode synchrone traditionnelle.

Si pour une raison quelconque, cela n'est pas possible (par exemple, vous implémentez une interface asynchrone) renvoyant une tâche terminée à l'aide de Task.FromResult ou encore mieux dans ce cas Task.CompletedTask (ajouté dans .NET 4.6) est bien meilleur que d'utiliser Task.Run dans l'implémentation:

public virtual Task CreateAsync(TUser user)
{
    // ...
    return Task.CompletedTask;
}

Si le consommateur de votre API tient beaucoup à ce que la méthode de retour Task- ne fonctionne pas de manière synchrone, il peut utiliser Task.Run eux-mêmes pour s'assurer.

Vous devez garder à l'esprit que les méthodes asynchrones peuvent avoir une partie synchrone considérable (la partie avant la première attente) même si elles continuent finalement de manière asynchrone. Vous ne pouvez pas supposer que les méthodes asynchrones renvoient immédiatement un Task.

19
i3arnon

Task.FromResult ne crée ni n'exécute réellement une tâche, mais il enveloppe simplement le résultat renvoyé dans un objet de tâche. Je l'ai personnellement utilisé dans Unit Tests où j'ai besoin de simuler les méthodes Async et bien sûr, je ne voudrais pas exécuter de tâches réelles dans les tests unitaires.

Outre Task.Run créera réellement une tâche et exécutera une tâche sur TaskScheduler. Il n'est pas recommandé d'utiliser Task.Run lorsque vous effectuez une programmation Async. Utilisez plutôt await sur les tâches. Voir quelques à faire et à ne pas faire des tâches par Stephen Cleary.

10
vendettamit