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 sake ne peut pas être modifié). Si l'implémentation de LazyBar
s est inhabituelle en ce sens qu'elle s'exécute rapidement et de manière synchrone - quel est le meilleur moyen de renvoyer une tâche sans opération à partir de la méthode?
Je suis allé avec Task.Delay(0)
ci-dessous, mais j'aimerais savoir si cela a des effets indésirables sur les performances si la fonction s'appelle un lot (par souci d'argument, disons des centaines de fois par seconde ):
Delay(0)
différemment?return Task.Run(() => { });
serait différent?Y a-t-il un meilleur moyen?
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();
}
}
}
L'utilisation de Task.FromResult(0)
ou Task.FromResult<object>(null)
occasionnera moins de temps système que la création d'un Task
avec une expression sans opération. Lors de la création d'un Task
avec un résultat prédéterminé, aucun temps système supplémentaire n'est impliqué dans la planification.
Aujourd'hui, je recommanderais d'utiliser Task.CompletedTask pour accomplir cela.
Pour ajouter à réponse de Reed Copsey à propos de l'utilisation de _Task.FromResult
_, vous pouvez améliorer les performances encore davantage si vous mettez en cache la tâche déjà terminée car toutes les instances de tâches terminées sont identiques:
_public static class TaskExtensions
{
public static readonly Task CompletedTask = Task.FromResult(false);
}
_
Avec _TaskExtensions.CompletedTask
_, vous pouvez utiliser la même instance dans l’ensemble du domaine de l’application.
La dernière version du .Net Framework (v4.6) ajoute simplement cela avec la Task.CompletedTask
propriété statique
_Task completedTask = Task.CompletedTask;
_
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 Task
terminé.
À partir de la version 4.6, il y a maintenant Task.CompletedTask
qui est plus explicite dans son but, mais non seulement Task.Delay(0)
renvoie toujours une seule instance mise en cache, il retourne l'instance identique unique mise en cache, comme Task.CompletedTask
.
La nature en cache de ni est garantie de rester constante, mais en tant qu'optimisations dépendantes de l'implémentation qui dépendent uniquement de l'implémentation (c'est-à-dire qu'elles fonctionneraient toujours correctement si l'implémentation changeait pour quelque chose qui était toujours valide) Task.Delay(0)
était mieux que la réponse acceptée.
Récemment rencontré ceci et continuait à recevoir des avertissements/erreurs sur la méthode étant annulée.
Nous sommes en train de calmer le compilateur et cela le clarifie:
public async Task MyVoidAsyncMethod()
{
await Task.CompletedTask;
}
Cela rassemble le meilleur de tous les conseils donnés jusqu'ici. Aucune déclaration de retour n'est nécessaire sauf si vous faites réellement quelque chose dans la méthode.
Quand vous devez retourner le type spécifié:
Task.FromResult<MyClass>(null);
Je préfère la solution Task completedTask = Task.CompletedTask;
de .Net 4.6, mais une autre approche consiste à marquer la méthode de manière asynchrone et à renvoyer une valeur nulle:
public async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
{
}
Vous recevrez un avertissement (CS1998 - Fonction asynchrone sans expression d'attente), mais il est prudent de l'ignorer dans ce contexte.
return Task.CompletedTask; // this will make the compiler happy