When to Use Task.Delay, When to Use Thread.Sleep

When to use Task.Delay, when to use Thread.Sleep?

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.

Diference between task.wait , Task.delay and thread.sleep

Basically Wait() and Sleep() are both thread blocking operations,in other words they force a thread to sit idle instead of doing work elsewhere. Delay on other hand uses a timer internally that releases the thread in use until the delay is complete.

There is much more that can be said about these three functions so here's a small sample set for further reading.

More Info

  • There is no thread
  • Don't Block on Async Code
  • When to use Task.Delay, when to use Thread.Sleep?
  • Thread.Sleep vs Task.Delay?
  • Thread.Sleep is a sign of a poorly designed program.

public class WaitSleepDelay
{
/// <summary>
/// The thread is released and is alerted when the delay finishes
/// </summary>
/// <returns></returns>
public async Task Delay()
{
//This Code Executes
await Task.Delay(1000);
//Now this code runs after 1000ms
}

/// <summary>
/// This blocks the currently executing thread for the duration of the delay.
/// This means that the thread is held hostage doing nothing
/// instead of being released to do more work.
/// </summary>
public void Sleep()
{
//This Code Executes
Thread.Sleep(1000);
//Now this code runs after 1000ms
}

/// <summary>
/// This blocks the currently executing thread for the duration of the delay
/// and will deadlock in single threaded sync context e.g. WPF, WinForms etc.
/// </summary>
public void Wait()
{
//This Code Executes
Task.Delay(1000).Wait();
//This code may never execute during a deadlock
}
}

Thread.Sleep vs Task.Delay?

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.

Task.Delay vs Thread.Sleep difference

The issue here is scoping. The i you are using inside the Task.Run(() => TheadFunc(i)) is not a new integer, but rather, since it's a delegate, the value of i will only be retrieved once the delegate is executed.

This leads to i being 4 in all cases, since your for-loop increases it that much. But since you do not have an if-condition for a value of 4, it won't perform any delays with Thread.Sleep(...).

Thread.Sleep(2500) vs. Task.Delay(2500).Wait()

Using Wait on an uncompleted task is indeed blocking the thread until the task completes.

Using Thread.Sleep is clearer since you're explicitly blocking a thread instead of implicitly blocking on a task.

The only way using Task.Delay is preferable is that it allows using a CancellationToken so you can cancel the block if you like to.

Should I always use Task.Delay instead of Thread.Sleep?

There's never an advantage in replacing Thread.Sleep(1000); in Task.Delay(1000).Wait();. If you want to wait synchronously just use Thread.Sleep.

If you really only have a single thread and planning to keep it that way, then you can use Thread.Sleep. However, I would still use Task.Delay as it's preferable in most cases and so it's a good pattern. I would only block at very top when you can't use async anymore, and even then I would suggest using some kind of AsyncContext.

You can also use a System.Threading.Timer directly* instead of Task.Delay however you should keep in mind that the timer executes every interval and doesn't wait for the actual operation to complete, so if ExternalServiceIsReady takes more than the interval you can have multiple calls to that service concurrently.

An even better solution would be to replace the polling of the external service with an asynchronous operation so the service can notify you when it's ready instead of you asking it every second (that isn't always possible as it depends on the service):

await ExternalServiceIsReadyAsync();

* Task.Delay uses a System.Threading.Timer internally which also has a resolution of ~15ms.

Thread.Sleep x Task.Delay

They do quite different things.

Thread.Sleep causes the currently executing thread to just stop running for a given amount of time. It probably doesn't consume additional memory or do much processing, but the entire time it is sleeping it uses a system Thread, which is a limited resource. You could starve your application of threads, or cause another Thread to be created elsewhere, which has big performance impact. But, works great if that one thread is the only thing running on your computer.

Task.Delay creates a Task that will be dormant for the given amount of time and then finish. If you use in an asynchronous method, this allows you to return the thread to the caller, which can use it for something else until the timer is up. This is much more efficient when you have multiple threads, such as in a web server environment or doing a lot of database reads. However, you must use await, otherwise the task is created but then ignored, so it doesn't delay anything. And to use await, you must be in an asynchronous method in an asynchronous stack. You could call Wait() on the task, but that still locks the thread so might as well use Sleep at that point.

It looks like you have some worker threads of some kind. Better to make those tasks and they use .WaitAll on WhenAll or WhenAny to wait until one is actually finished.

Why does Thread.Sleep work but Task.Delay does not?

Your Lock method doesn't understand async delegates, so the async delegate you are trying to pass as argument is treated as async void. Async voids are problematic in all sorts of ways and should be avoided, unless they are used for their intended purpose, as event handlers.

To ensure that your method understand async delegates you must create an overload that accepts a Func<Task> as argument:

private Task Lock(Func<Task> func)
{
return Task.Run(func);
}

Notice that the func argument can be passed directly to Task.Run, because this method understands async delegates too. Not all built-in methods understand async delegates, with notable examples the Task.Factory.StartNew and Parallel.ForEach. You must be cautious every time you add the async modifier in a delegate. You must be sure that the called method understands async delegates, or else you may end up with async voids and the havoc they create.

Task.Delay vs Thread.Sleep for suspending System.Timers.Timer's job

It depends on what your goal is.

Thread.Sleep will block the system thread that is in use. By default System.Timers.Timer uses the system thread-pool, meaning you wouldn't block your main thread, and the use of Thread.Sleep probably will execute concurrently with the rest of your program. If you desire is to use (and therefore block) a specific thread, you will need use the SynchronizingObject property, in coordination with Thread.Sleep.

Task.Delay will provide a logical delay without blocking the current thread. This is the best approach, unless you have a reason to block a specific thread. Keep in mind you'll need to make your event handler asynchronous to use Task.Delay:

 timer.Elapsed += async (sender, args) =>
{
for (int i = 0; i < 15; i++)
{
await Task.Delay(1000);
...
}
}


Related Topics



Leave a reply



Submit