Se la mia interfaccia deve restituire Attività qual è il modo migliore per avere un’implementazione senza operazioni?

Nel codice seguente, a causa dell’interfaccia, la class LazyBar deve restituire un’attività dal suo metodo (e non può essere modificata per motivi di argomenti). Se l’implementazione di LazyBar è inusuale in quanto avviene in modo rapido e sincrono, qual è il modo migliore per restituire un’attività non operativa dal metodo?

Sono andato con Task.Delay(0) qui sotto, tuttavia vorrei sapere se questo ha effetti collaterali di prestazioni se la funzione è chiamata molto (per ragioni di argomenti, diciamo centinaia di volte al secondo):

  • Questo zucchero sintattico non porta a qualcosa di grande?
  • Inizia a intasare il pool di thread della mia applicazione?
  • La mannaia del compilatore è abbastanza per gestire il Delay(0) diverso?
  • return Task.Run(() => { }); essere diverso?

C’è un modo migliore?

 using System.Threading.Tasks; namespace MyAsyncTest { internal interface IFooFace { Task WillBeLongRunningAsyncInTheMajorityOfImplementations(); } ///  /// An implementation, that unlike most cases, will not have a long-running /// operation in 'WillBeLongRunningAsyncInTheMajorityOfImplementations' ///  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(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’utilizzo di Task.FromResult(0) o Task.FromResult(null) comporta un sovraccarico minore rispetto alla creazione di un’attività con un’espressione non operativa. Quando si crea un’attività con un risultato predeterminato, non è previsto un sovraccarico di pianificazione.


Oggi raccomanderei di usare Task.CompletedTask per realizzare questo.

Per aggiungere alla risposta di Reed Copsey l’ utilizzo di Task.FromResult , è ansible migliorare ulteriormente le prestazioni se si memorizza nella cache l’attività già completata poiché tutte le istanze delle attività completate sono le stesse:

 public static class TaskExtensions { public static readonly Task CompletedTask = Task.FromResult(false); } 

Con TaskExtensions.CompletedTask puoi utilizzare la stessa istanza nell’intero dominio dell’app.


L’ ultima versione di .Net Framework (v4.6) aggiunge solo quella con la proprietà statica Task.CompletedTask

 Task completedTask = Task.CompletedTask; 

Task.Delay(0) come era nella risposta era un buon approccio, in quanto è una copia memorizzata nella cache di un’attività completata.

A partire da 4,6 ora c’è Task.CompletedTask che è più esplicito nel suo scopo, ma non solo Task.Delay(0) restituisce ancora una singola istanza memorizzata nella cache, restituisce la stessa istanza memorizzata nella cache come Task.CompletedTask .

La natura cache di nessuno dei due è garantita per rimanere costante, ma come ottimizzazioni dipendenti dall’implementazione che dipendono solo dall’implementazione come ottimizzazioni (cioè funzionano ancora correttamente se l’implementazione è cambiata in qualcosa che era ancora valido) l’uso di Task.Delay(0) era migliore della risposta accettata.

Recentemente è stato riscontrato e ha continuato a ricevere avvertimenti / errori relativi alla nullità del metodo.

Siamo nel business di placare il compilatore e questo lo risolve:

  public async Task MyVoidAsyncMethod() { await Task.CompletedTask; } 

Questo riunisce il meglio di tutti i consigli qui finora. Nessuna dichiarazione di reso è necessaria a meno che tu non stia facendo qualcosa nel metodo.

Preferisco l’ Task completedTask = Task.CompletedTask; soluzione di .Net 4.6, ma un altro approccio è quello di contrassegnare il metodo async e return void:

  public async Task WillBeLongRunningAsyncInTheMajorityOfImplementations() { } 

Riceverai un avviso (CS1998 – Funzione asincrona senza espressione di attesa), ma questo è sicuro da ignorare in questo contesto.