Dans le code ci-dessous, en raison de l'interface, la classe LazyBar
doit renvoyer une tâche à partir de sa méthode (et pour des arguments, le sake ne peut pas être changé). Si l' LazyBar
implémentation de S est inhabituelle en ce sens qu'elle s'exécute rapidement et de manière synchrone - quelle est la meilleure façon de renvoyer une tâche sans opération à partir de la méthode?
Je suis allé avec Task.Delay(0)
ci - dessous, mais je voudrais savoir si cela a des effets secondaires performances si la fonction est appelée beaucoup (pour l' amour des arguments, dire des centaines de fois par seconde):
- Ce sucre syntaxique se déroule-t-il en quelque chose de grand?
- Cela commence-t-il à obstruer le pool de threads de mon application?
- Le couperet du compilateur est-il suffisant pour traiter
Delay(0)
différemment? - Serait-
return Task.Run(() => { });
ce différent?
Y a-t-il une meilleure façon?
using System.Threading.Tasks;
namespace MyAsyncTest
{
internal interface IFooFace
{
Task WillBeLongRunningAsyncInTheMajorityOfImplementations();
}
/// <summary>
/// An implementation, that unlike most cases, will not have a long-running
/// operation in 'WillBeLongRunningAsyncInTheMajorityOfImplementations'
/// </summary>
internal class LazyBar : IFooFace
{
#region IFooFace Members
public Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
{
// First, do something really quick
var x = 1;
// Can't return 'null' here! Does 'Task.Delay(0)' have any performance considerations?
// Is it a real no-op, or if I call this a lot, will it adversely affect the
// underlying thread-pool? Better way?
return Task.Delay(0);
// Any different?
// return Task.Run(() => { });
// If my task returned something, I would do:
// return Task.FromResult<int>(12345);
}
#endregion
}
internal class Program
{
private static void Main(string[] args)
{
Test();
}
private static async void Test()
{
IFooFace foo = FactoryCreate();
await foo.WillBeLongRunningAsyncInTheMajorityOfImplementations();
return;
}
private static IFooFace FactoryCreate()
{
return new LazyBar();
}
}
}
Task.FromResult<object>(null)
.Réponses:
L'utilisation de
Task.FromResult(0)
ouTask.FromResult<object>(null)
entraînera moins de frais généraux que la création d'unTask
avec une expression sans op. Lors de la création d'unTask
avec un résultat prédéterminé, aucune surcharge de planification n'est impliquée.Aujourd'hui, je recommanderais d'utiliser Task.CompletedTask pour y parvenir.
la source
return default(YourReturnType);
Task.CompletedTask
pourrait faire l'affaire! (mais nécessite .net 4.6)Pour compléter la réponse de Reed Copsey à propos de l'utilisation
Task.FromResult
, vous pouvez encore améliorer les performances si vous mettez en cache la tâche déjà terminée car toutes les instances de tâches terminées sont les mêmes:Avec,
TaskExtensions.CompletedTask
vous pouvez utiliser la même instance dans l'ensemble du domaine d'application.La dernière version du .Net Framework (v4.6) ajoute juste cela avec la
Task.CompletedTask
propriété statiquela source
public Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
aussi bien quepublic async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
. Donc, nous pouvons soitreturn CompletedTask;
ouawait CompletedTask;
. Quoi de plus préférable (peut-être plus efficace ou plus congruent)?Task.Delay(0)
comme dans la réponse acceptée était une bonne approche, car il s'agit d'une copie mise en cache d'un terminéTask
.À partir de la version 4.6, il existe désormais un objet
Task.CompletedTask
plus explicite, mais non seulement ilTask.Delay(0)
renvoie toujours une seule instance en cache, mais il renvoie la même instance en cache unique que luiTask.CompletedTask
.La nature en cache de n'est garanti à rester constant, mais comme Optimisations dépendant de l' implémentation qui ne dépendent que la mise en œuvre Optimisations (qui est, ils avaient toujours correctement si la mise en œuvre a changé quelque chose qui était encore valide) l'utilisation de
Task.Delay(0)
était mieux que la réponse acceptée.la source
Task.CompletedTask
ne peut pas être utilisé dans un projet PCL, même si j'ai défini la version .net sur 4.6 (profil 7), juste testée dans VS2017.Task.CompletedTask => Task.Delay(0);
pour le supporter, donc je ne sais pas du haut de ma tête.J'ai récemment rencontré cela et j'ai continué à recevoir des avertissements / erreurs concernant l'annulation de la méthode.
Nous sommes en train de calmer le compilateur et cela clarifie les choses:
Cela rassemble le meilleur de tous les conseils jusqu'ici. Aucune déclaration de retour n'est nécessaire à moins que vous ne fassiez réellement quelque chose dans la méthode.
la source
public Task MyVoidAsyncMethod() {}
est complètement la même que la méthode ci-dessus. S'il existe un cas d'utilisation pour l'utiliser comme ceci, veuillez ajouter le code supplémentaire.la source
Lorsque vous devez renvoyer le type spécifié:
la source
Je préfère la
Task completedTask = Task.CompletedTask;
solution de .Net 4.6, mais une autre approche consiste à marquer la méthode async et à retourner void:Vous obtiendrez un avertissement (CS1998 - Fonction asynchrone sans expression d'attente), mais cela peut être ignoré dans ce contexte.
la source
Si vous utilisez des génériques, toutes les réponses nous donneront une erreur de compilation. Vous pouvez utiliser
return default(T);
. Échantillon ci-dessous pour expliquer davantage.la source
la source