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?
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
.
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.