If my interface must return Task what is the best way to have a no-operation implementation?

C#.NetTask Parallel-LibraryAsync AwaitThreadpool

C# Problem Overview


In the code below, due to the interface, the class LazyBar must return a task from its method (and for argument's sake can't be changed). If LazyBars implementation is unusual in that it happens to run quickly and synchronously - what is the best way to return a No-Operation task from the method?

I have gone with Task.Delay(0) below, however I would like to know if this has any performance side-effects if the function is called a lot (for argument's sake, say hundreds of times a second):

  • Does this syntactic sugar un-wind to something big?
  • Does it start clogging up my application's thread pool?
  • Is the compiler cleaver enough to deal with Delay(0) differently?
  • Would return Task.Run(() => { }); be any different?

Is there a better way?

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();
        }
    }
}

C# Solutions


Solution 1 - C#

Today, I would recommend using Task.CompletedTask to accomplish this.


Pre .net 4.6:

Using Task.FromResult(0) or Task.FromResult<object>(null) will incur less overhead than creating a Task with a no-op expression. When creating a Task with a result pre-determined, there is no scheduling overhead involved.

Solution 2 - C#

To add to Reed Copsey's answer about using Task.FromResult, you can improve performance even more if you cache the already completed task since all instances of completed tasks are the same:

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

With TaskExtensions.CompletedTask you can use the same instance throughout the entire app domain.


The latest version of the .Net Framework (v4.6) adds just that with the Task.CompletedTask static property

Task completedTask = Task.CompletedTask;

Solution 3 - C#

Task.Delay(0) as in the accepted answer was a good approach, as it is a cached copy of a completed Task.

As of 4.6 there's now Task.CompletedTask which is more explicit in its purpose, but not only does Task.Delay(0) still return a single cached instance, it returns the same single cached instance as does Task.CompletedTask.

The cached nature of neither is guaranteed to remain constant, but as implementation-dependent optimisations that are only implementation-dependent as optimisations (that is, they'd still work correctly if the implementation changed to something that was still valid) the use of Task.Delay(0) was better than the accepted answer.

Solution 4 - C#

Recently encountered this and kept getting warnings/errors about the method being void.

We're in the business of placating the compiler and this clears it up:

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

This brings together the best of all the advice here so far. No return statement is necessary unless you're actually doing something in the method.

Solution 5 - C#

return Task.CompletedTask; // this will make the compiler happy

Solution 6 - C#

When you must return specified type:

Task.FromResult<MyClass>(null);

Solution 7 - C#

I prefer the Task completedTask = Task.CompletedTask; solution of .Net 4.6, but another approach is to mark the method async and return void:

    public async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
    {
    }

You'll get a warning (CS1998 - Async function without await expression), but this is safe to ignore in this context.

Solution 8 - C#

If you are using generics, all answer will give us compile error. You can use return default(T);. Sample below to explain further.

public async Task<T> GetItemAsync<T>(string id)
            {
                try
                {
                    var response = await this._container.ReadItemAsync<T>(id, new PartitionKey(id));
                    return response.Resource;
                }
                catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
                {
                    
                    return default(T);
                }

            }

Solution 9 - C#

return await Task.FromResult(new MyClass());

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJon ReaView Question on Stackoverflow
Solution 1 - C#Reed CopseyView Answer on Stackoverflow
Solution 2 - C#i3arnonView Answer on Stackoverflow
Solution 3 - C#Jon HannaView Answer on Stackoverflow
Solution 4 - C#Alexander TrauzziView Answer on Stackoverflow
Solution 5 - C#XinView Answer on Stackoverflow
Solution 6 - C#trashmaker_View Answer on Stackoverflow
Solution 7 - C#Remco te WierikView Answer on Stackoverflow
Solution 8 - C#Karthikeyan VKView Answer on Stackoverflow
Solution 9 - C#J.M.HView Answer on Stackoverflow