Are there good rule(s) for when to use Task.Delay versus Thread.Sleep?
Specifically, is there a minimum value to provide for one to be effective/efficient over the other?
Lastly, since Task.Delay causes context-switching on a async/await state machine, is there an overhead of using it?
Use Thread.Sleep
when you want to block the current thread.
Use Task.Delay
when you want a logical delay without blocking the current thread.
Efficiency should not be a paramount concern with these methods. Their primary real-world use is as retry timers for I/O operations, which are on the order of seconds rather than milliseconds.
The biggest difference between Task.Delay
and Thread.Sleep
is that Task.Delay
is intended to run asynchronously. It does not make sense to use Task.Delay
in synchronous code. It is a VERY bad idea to use Thread.Sleep
in asynchronous code.
Normally you will call Task.Delay()
with the await
keyword:
await Task.Delay(5000);
or, if you want to run some code before the delay:
var sw = new Stopwatch();
sw.Start();
Task delay = Task.Delay(5000);
Console.WriteLine("async: Running for {0} seconds", sw.Elapsed.TotalSeconds);
await delay;
Guess what this will print? Running for 0.0070048 seconds. If we move the await delay
above the Console.WriteLine
instead, it will print Running for 5.0020168 seconds.
Let's look at the difference with Thread.Sleep
:
class Program
{
static void Main(string[] args)
{
Task delay = asyncTask();
syncCode();
delay.Wait();
Console.ReadLine();
}
static async Task asyncTask()
{
var sw = new Stopwatch();
sw.Start();
Console.WriteLine("async: Starting");
Task delay = Task.Delay(5000);
Console.WriteLine("async: Running for {0} seconds", sw.Elapsed.TotalSeconds);
await delay;
Console.WriteLine("async: Running for {0} seconds", sw.Elapsed.TotalSeconds);
Console.WriteLine("async: Done");
}
static void syncCode()
{
var sw = new Stopwatch();
sw.Start();
Console.WriteLine("sync: Starting");
Thread.Sleep(5000);
Console.WriteLine("sync: Running for {0} seconds", sw.Elapsed.TotalSeconds);
Console.WriteLine("sync: Done");
}
}
Try to predict what this will print...
async: Starting async: Running for 0.0070048 seconds sync: Starting async: Running for 5.0119008 seconds async: Done sync: Running for 5.0020168 seconds sync: Done
Also, it is interesting to notice that Thread.Sleep
is far more accurate, ms accuracy is not really a problem, while Task.Delay
can take 15-30ms minimal. The overhead on both functions is minimal compared to the ms accuracy they have (use Stopwatch
Class if you need something more accurate). Thread.Sleep
still ties up your Thread, Task.Delay
release it to do other work while you wait.
Thread.Sleep()
consumes an entire thread that could otherwise be used elsewhere. If many tasks are run with Thread.Sleep(), there's a high chance of exhausting all threadpool threads and seriously hindering performance.
async
methods as they are encouraged to be used. It's basically just a bad idea to run Thread.Sleep()
in a threadpool thread, not a bad idea in general. After all, there's TaskCreationOptions.LongRunning
when going the (albeit discouraged) Task.Factory.StartNew()
route.
Thread.Sleep
in async code for very low values, e.g. Thread.Sleep(50)
(for some HW communication).
Tasl.Delay
uses the system timer. Since "The system clock "ticks" at a constant rate.", the tick speed of the system timer is about 16ms, any delay you request will be rounded to a number of ticks of the system clock, offset by the time till the first tick. See the msdn documentation on Task.Delay
docs.microsoft.com/en-us/dotnet/api/… and scroll down to remarks.
I want to add something. Actually, Task.Delay
is a timer based wait mechanism. If you look at the source you would find a reference to a Timer
class which is responsible for the delay. On the other hand Thread.Sleep
actually makes current thread to sleep, that way you are just blocking and wasting one thread. In async programming model you should always use Task.Delay()
if you want something(continuation) happen after some delay.
if the current thread is killed and you use Thread.Sleep
and it is executing then you might get a ThreadAbortException
. With Task.Delay
you can always provide a cancellation token and gracefully kill it. Thats one reason I would choose Task.Delay
. see http://social.technet.microsoft.com/wiki/contents/articles/21177.visual-c-thread-sleep-vs-task-delay.aspx
I also agree efficiency is not paramount in this case.
await Task.Delay(5000)
. When I kill the task I get TaskCanceledException
(and suppress it) but my thread is still alive. Neat! :)
Delayed
would be a better name for Task.Delay
- because it doesn't delay an existing task but rather creates a new 'delayed' one which on the other hand can be awaited and can cause suspension to the current task's body. It is essentially a Timer but without a callback/body.
Awaiting a delayed task creates a new item in async message queue and doesn't block any threads. The same thread where the await is called will proceed working on other tasks should there be any, and will return to the await point after the timeout (or when the preceding items in queue are complete). Tasks under the hood use Threads - there can be many Tasks scheduled and executed in a single thread. On the other hand if you happen to call Thread.Sleep()
the thread will block, i.e. it will be out of play for the amount of time asked and won't process any async messages from the queue.
In .NET there're 2 major approaches to parallelism. The old one with Threads, ThreadPools etc. And the new one, based on Tasks, async/await, TPL. As a rule of thumb you don't mix APIs from these two universes.
It is also worth to mention that Thread.Sleep(1) will fire GC faster.
This is purely based mine & team member observations. Lets assume that you have service which creates new task every for specific request (approx. 200-300 ongoing) and this task contains many weak references in flow. The task is working like state machine so we were firing the Thread.Sleep(1) on change state and by doing so we managed to optimize utilization of memory in the application - like I said before - this will makes GC to fire faster. It doesn't make so much difference in low memory consumption services (<1GB).
I had a long argument with a colleague about this and he proved to me that there are significant differences beyond what the top answer currently shows. If you await Task.Delay(SomeMilliseconds)
you can actually release callers other than your immediate parent on the stack:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Started " + Thread.CurrentThread.ManagedThreadId);
DoSomething1();
Console.WriteLine("Finished " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(6000);
}
static async void DoSomething1()
{
Console.WriteLine("DoSomething1 Started " + Thread.CurrentThread.ManagedThreadId);
var result = await DoSomething2();
Console.WriteLine("DoSomething1 Finished " + Thread.CurrentThread.ManagedThreadId);
}
static async Task<int> DoSomething2()
{
Console.WriteLine("DoSomething2 Started " + Thread.CurrentThread.ManagedThreadId);
await Task.Delay(5000); // Will block DoSomething1 but release Main
//Thread.Sleep(5000); // Will block everything including Main
//await Task.FromResult(5); // Will return immediately (just for comparison)
//await Task.Delay(0); // What will it do, can you guess?
Console.WriteLine("DoSomething2 Finished " + Thread.CurrentThread.ManagedThreadId);
return 0;
}
}
}
Play with this code and observe the different effects of using Delay
or using Sleep
. The explanation is beyond the scope of this answer but can be summed up as "async functions don't start another thread until they await something that can't be immediately run (or the outcome determined)". This is the output:
Started 1
DoSomething1 Started 1
DoSomething2 Started 1
Finished 1
DoSomething2 Finished 4
DoSomething1 Finished 4
This isn't about DoSomething1();
in Main
being fire and forget. You can prove that by using the Sleep
. Also observe that when DoSomething2 "returns" from Task.Delay, it's running on a different thread.
This stuff is much smarter than I gave it credit for, believing that await
just started a new thread to do stuff. I still don't pretend to understand it all, but the counter-intuitive result above shows there's much more going on under the hood than just starting threads to run code.
async void DoSomething1()
<== Avoid async void. It is intended for event handlers, and the DoSomething1
doesn't look like an event handler.
My opinion,
Task.Delay()
is asynchronous. It doesn't block the current thread. You can still do other operations within current thread. It returns a Task return type (Thread.Sleep()
doesn't return anything ). You can check if this task is completed(use Task.IsCompleted
property) later after another time-consuming process.
Thread.Sleep()
doesn't have a return type. It's synchronous. In the thread, you can't really do anything other than waiting for the delay to finish.
As for real-life usage, I have been programming for 15 years. I have never used Thread.Sleep()
in production code. I couldn't find any use case for it. Maybe that's because I mostly do web application development.
In an async program, the difference between
await task.Delay()
//and
thread.sleep
is nominal in a simple app, one might be more cancellable, one might be more accurate, one might be a tiny bit faster... but at the end of the day, both do the same thing, they block the executing code...
Here are the results:
1 00:00:00.0000767
Not Delayed.
1 00:00:00.2988809
Delayed 1 second.
4 00:00:01.3392148
Delayed 3 second.
5 00:00:03.3716776
Delayed 9 seconds.
5 00:00:09.3838139
Delayed 10 seconds
4 00:00:10.3411050
4 00:00:10.5313519
From this code:
var sw = new Stopwatch();
sw.Start();
Console.WriteLine($"{sw.Elapsed}");
var asyncTests = new AsyncTests();
var go1 = asyncTests.WriteWithSleep();
var go2 = asyncTests.WriteWithoutSleep();
await go1;
await go2;
sw.Stop();
Console.WriteLine($"{sw.Elapsed}");
Stopwatch sw1 = new Stopwatch();
Stopwatch sw = new Stopwatch();
public async Task WriteWithSleep()
{
sw.Start();
var delayedTask = Task.Delay(1000);
Console.WriteLine("Not Delayed.");
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} {sw.Elapsed}");
await delayedTask;
Console.WriteLine("Delayed 1 second.");
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} {sw.Elapsed}");
Thread.Sleep(9000);
Console.WriteLine("Delayed 10 seconds");
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} {sw.Elapsed}");
sw.Stop();
}
public async Task WriteWithoutSleep()
{
await Task.Delay(3000);
Console.WriteLine("Delayed 3 second.");
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} {sw.Elapsed}");
await Task.Delay(6000);
Console.WriteLine("Delayed 9 seconds.");
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} {sw.Elapsed}");
}
Sleep acts the same way as an immediate await, except it blocks the thread. A task that is assigned to a var may cause a thread switch when it is finally awaited. In this example, it looks like the code starts on thread 1, then creates thread 5 for WriteWithoutSleep(), but continues executing on thread 1 for ThreadWithSleep() until delayedTask is awaited. At that moment, thread 1's code flows into thread 4 and further execution in Main is now on thread 4; thread 1 is for lack of a better word thrown away.
All of the above answers are very valuable. However, In a simple console app, it doesn't seem to matter except maybe academically over the course of several runs which you use if you immediately await your Task.Delay() and don't intend to use cancellation tokens;
In a complex app, putting threads to sleep vs. jumping from thread to thread due to creating tasks and awaiting them later vs. immediate awaits could be points of consideration.
Finally, putting a Process.GetCurrentProcess().Threads.Count at the beginning of a console app (at least mine) produced 13 threads in debugger mode. After the awaited calls, I had 17 threads in debugger mode in visual studio. I've read that ConsoleApps have only 3 threads and the rest are debugger threads, but running the consoleApp without debugging in visual studio resulted in 8 and then 14 threads. Running it outside visual studio resulted in 8 then 14 threads.
Copying the code and pasting it just afterwards had the same number of the threads, 8,14 and everything stayed on threads 4 and 5. The second thread.sleep and task.delays did not cause thread jumps. And all of this research is to propose: while thread.sleep will block a thread and task.delay will not and has a cancellation token, unless your app is pretty complex, it really doesn't matter as on the surface: task.delay and thread.sleep do pretty much the same thing.
await task.Delay()
doesn't block any thread, and if you want I can easily prove it.
await task.Delay()
doesn't block a thread. I create 100,000 tasks that are awaiting a await task.Delay(1000)
, and then wait them all to complete. They all complete after 1000 msec. If each await task.Delay()
was blocking a thread, I would need 100,000 threads. Each thread requires 1 MB of RAM, so I would need 100 GB RAM. My machine has only 4GB RAM. So it's impossible that my app created so many threads.
await Task.Delay(1000)
in the async handler of a Click
event, the same thread will run the code after the await
. That's because there is a special SynchronizationContext
installed on the UI thread of all WinForms applications.
Success story sharing
Thread.Sleep
will block the current thread which causes a context switch. If you're using a thread pool this could also cause a new thread to be allocated. Both operations are quite heavy whereas the cooperative multi-tasking provided byTask.Delay
etc is designed to avoid all of that overhead, maximize throughput, allow cancellation, and provide cleaner code.onesi: I would use
Thread.Sleep` to wait inside a synchronous method. However, I never do this in production code; in my experience, everyThread.Sleep
I've ever seen has been indicative of some kind of design issue that needs to be properly fixed.