Reputation: 61656
Does Delay(0)
always get inlined? In my experience, it does:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
class Program
{
static async Task Test()
{
await Task.Yield();
Console.WriteLine("after Yield(), thread: {0}", Thread.CurrentThread.ManagedThreadId);
await Task.Delay(0);
Console.WriteLine("after Delay(0), thread: {0}", Thread.CurrentThread.ManagedThreadId);
await Task.Delay(100);
Console.WriteLine("after Delay(100), thread: {0}", Thread.CurrentThread.ManagedThreadId);
}
static void Main(string[] args)
{
Console.WriteLine("Main thread: {0}", Thread.CurrentThread.ManagedThreadId);
Test().Wait();
}
}
}
This is a console app, thus the thread pool is used for continuation. The output:
Main thread: 11
after Yield(), thread: 7
after Delay(0), thread: 7
after Delay(100), thread: 6
Upvotes: 24
Views: 11929
Reputation: 1062502
Yes, it does. A check of the IL in reflector shows (among other logic):
if (millisecondsDelay == 0)
{
return CompletedTask;
}
So yes, it will hand you back an already-completed task in this case.
Note that the implementation of await
includes checks that ensure that an already-completed task doesn't cause an additional context switch, so yes: your code will keep running without pausing for breath here.
Returning an already completed task is a recommended trick when the answer is already known / available synchronously; it is also common to cache Task
s for common result values.
Upvotes: 14
Reputation: 239636
Inside Task.Delay
, it looks like this (the single parameter (int)
version just calls the below version):
[__DynamicallyInvokable]
public static Task Delay(int millisecondsDelay, CancellationToken cancellationToken)
{
if (millisecondsDelay < -1)
{
throw new ArgumentOutOfRangeException("millisecondsDelay", Environment.GetResourceString("Task_Delay_InvalidMillisecondsDelay"));
}
if (cancellationToken.IsCancellationRequested)
{
return FromCancellation(cancellationToken);
}
if (millisecondsDelay == 0)
{
return CompletedTask;
}
DelayPromise state = new DelayPromise(cancellationToken);
if (cancellationToken.CanBeCanceled)
{
state.Registration = cancellationToken.InternalRegisterWithoutEC(delegate (object state) {
((DelayPromise) state).Complete();
}, state);
}
if (millisecondsDelay != -1)
{
state.Timer = new Timer(delegate (object state) {
((DelayPromise) state).Complete();
}, state, millisecondsDelay, -1);
state.Timer.KeepRootedWhileScheduled();
}
return state;
}
As you can hopefully see:
if (millisecondsDelay == 0)
{
return CompletedTask;
}
Which means it always returns a completed task, and therefore your code will always continue running past that particular await
line.
Upvotes: 26