Thread.Sleep vs Task.Delay?

C#Multithreading.Net 4.0.Net 4.5

C# Problem Overview


I know that Thread.Sleep blocks a thread.

But does Task.Delay also block? Or is it just like Timer which uses one thread for all callbacks (when not overlapping)?

(this question doesn't cover the differences)

C# Solutions


Solution 1 - C#

The documentation on MSDN is disappointing, but decompiling Task.Delay using Reflector gives more information:

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

Basically, this method is just a timer wrapped inside of a task. So yes, you can say it's just like timer.

Solution 2 - C#

No, the Task.Delay doesn't block the current thread. It can be used to block it, but it doesn't do it by itself, and it's actually rarely used as a synchronous blocker. All it actually does is to return a Task that will complete after the specified amount of time:

Task task = Task.Delay(1000); // The task will complete after 1,000 milliseconds.

Typically this task is then waited asynchronously with the await keyword, inside an async method:

await task; // Suspends the async method, but doesn't block the thread.

The await keyword suspends the current execution flow (async method) until the awaitable completes. No thread is blocked while the execution flow is suspended.

It is also possible to block the current thread until the task completes, by using the synchronous Wait method.

task.Wait(); // Blocks the thread.

If you would like to see an experimental demonstration that the await Task.Delay() doesn't block a thread, here is one. The program below creates a huge number of tasks, where each task awaits a internally a Task.Delay(1000). Then the number of threads used by the current process is printed in the console, and finally all of the tasks are awaited:

Task[] tasks = Enumerable.Range(1, 100_000).Select(async _ =>
{
    await Task.Delay(1000);
}).ToArray();
Console.WriteLine($"Tasks: {tasks.Count(t => t.IsCompleted):#,0} / {tasks.Length:#,0}");
Thread.Sleep(500);
Console.WriteLine($"Threads.Count: {Process.GetCurrentProcess().Threads.Count:#,0}");
await Task.WhenAll(tasks);
Console.WriteLine($"Tasks: {tasks.Count(t => t.IsCompleted):#,0} / {tasks.Length:#,0}");

Output:

Tasks: 0 / 100,000
Threads.Count: 9
Tasks: 100,000 / 100,000

Live demo.

The program completes after just 1 second, and reports that during its peak it used a total of 9 threads. If each of the 100,000 tasks blocked a thread, we would expect to see 100,000 threads used at that point. Apparently this didn't happen.

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
QuestionRoyi NamirView Question on Stackoverflow
Solution 1 - C#Kevin GosseView Answer on Stackoverflow
Solution 2 - C#Theodor ZouliasView Answer on Stackoverflow