75

There is Task.Delay in .NET 4.5

How can I do the same in .NET 4.0?

Fulproof
  • 4,466
  • 6
  • 30
  • 50
  • 2
    [Thread.Sleep](http://msdn.microsoft.com/en-us/library/system.threading.thread.sleep.aspx)? – default Mar 11 '13 at 15:07
  • Have a look here: http://msdn.microsoft.com/en-us/library/hh194845.aspx Or use Thread.Sleep and add a reference to using System.Threading; – Max Mar 11 '13 at 15:08
  • possible duplicate: http://stackoverflow.com/q/4990602/ – default Mar 11 '13 at 16:57
  • 4
    @Default, for using Sleep() the task should have always: 1)spawned a separate thread, and 2)only one. No less and no more and 3)it cannot be reused. Nothing of this holds for a task. It is not a dupe, your link is about delaying a the start of a task. My question is about putting it to sleep at any moment after its start – Fulproof Mar 11 '13 at 17:30
  • @Mobstaa, I could not find any conceptual difference between my reference and yours. What was the point? – Fulproof Mar 11 '13 at 17:33
  • I'm confused, `Task.Delay()` does not put a task to sleep, it creates a new `Task`. If you wait on that, then it will put a task to sleep for the given amount of time. Is that what you're looking for? A `Task` that will complete after specified time? – svick Mar 11 '13 at 20:00
  • @Default Besides what was already written Thread.Sleep blocks a thread instead of releasing it back to thread pool, what would lead to thread starvation in a multi threaded system like ASP.Net. – user3285954 Nov 17 '16 at 13:31
  • @Max Task.Delay is not available in .Net 4.0. – user3285954 Nov 17 '16 at 13:32
  • @EliArbel: Why? I thought it is OK to use `Thread.Sleep` in synchronous code. – Isaac Aug 02 '17 at 22:10

9 Answers9

78

Use the Microsoft.Bcl.Async package from NuGet, it has TaskEx.Delay.

Eli Arbel
  • 22,391
  • 3
  • 45
  • 71
62

You can use a Timer to create a Delay method in 4.0:

public static Task Delay(double milliseconds)
{
    var tcs = new TaskCompletionSource<bool>();
    System.Timers.Timer timer = new System.Timers.Timer();
    timer.Elapsed+=(obj, args) =>
    {
        tcs.TrySetResult(true);
    };
    timer.Interval = milliseconds;
    timer.AutoReset = false;
    timer.Start();
    return tcs.Task;
}
Servy
  • 202,030
  • 26
  • 332
  • 449
  • thanks, I didn't specify but I am trying to add multithreading to my WPF application. Should I use there the Timer with Callback argument? And in `Callback()` definition use `Dispatcher.BeginInvoke()`? – Fulproof Mar 12 '13 at 03:40
  • @Fulproof You're not performing any UI interaction when the timer fires, so there's no reason to. – Servy Mar 12 '13 at 14:06
  • 1
    how to add cancellationToken to it, which Task.Delay provides? – Imran Rizvi Jul 11 '13 at 16:29
  • @ImranRizvi Add a continuation that does nothing and pass the cancellation token to that continuation. You could of course modify this method to take a cancellation token if you wanted, and just cancel the TCS when the token cancel event fires, if you feel up for making that modification. – Servy Jul 11 '13 at 16:31
  • 2
    Doesn't this create a race condition? If the timer object would happen to get garbage collected before the timer expires, it seems that `TrySetResult` would never get called. – Edward Brey Sep 16 '13 at 13:58
  • 10
    @EdwardBrey The `Timer` class specifically handles this internally to ensure that users of it don't need to hold onto a reference to it for it's lifetime. As long as the timer is currently running it adds a reference to itself from a rooted location and then removes it when it's no longer running. – Servy Sep 16 '13 at 14:01
  • @Servy, I know, this is pretty old question, but I gotta ask - why this solution doesn't explicitly dispose created `timer` instances? Shouldn't they be disposed? – Ilya Luzyanin Apr 13 '15 at 10:22
  • @IlyaLuzyanin The finalizer should take care of it, but you can explicitly dispose it in the event handler if you want. – Servy Apr 13 '15 at 14:10
  • @Servy: You should never depend on a finalizer to dispose of an `IDisposable`. It should always be disposed of explicitly unless there is specific documentation (e.g. as there is with `Task`) that states otherwise. – Adam Robinson May 29 '15 at 18:04
  • @AdamRobinson It all depends on context. It depends on what the object is, what the unmanaged resource is, what the consequences are for not cleaning it up, and the specifics of the program using it. Some cases are more important than others, but it's generally a good idea, yes. – Servy Jun 01 '15 at 12:50
  • Can you please explain if this is better than the answer provided by ['QrystaL'](https://stackoverflow.com/a/15342122/2803565) and why? – S.Serpooshan Jun 17 '19 at 12:32
26
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        Delay(2000).ContinueWith(_ => Console.WriteLine("Done"));
        Console.Read();
    }

    static Task Delay(int milliseconds)
    {
        var tcs = new TaskCompletionSource<object>();
        new Timer(_ => tcs.SetResult(null)).Change(milliseconds, -1);
        return tcs.Task;
    }
}

From the section How to implement Task.Delay in 4.0

QrystaL
  • 4,886
  • 2
  • 24
  • 28
  • 3
    I added the example to your answer, in case the link goes dead for some reason. – default Mar 11 '13 at 15:20
  • 2
    @Fulproof He wrote is using LinqPad, which adds an extension method to `object` that prints the value of it's `ToString` method out. Note that he doesn't use that, nor any other non-library methods in his actual implementation, just the example function that tests it out. – Servy Mar 11 '13 at 20:52
  • @Servy, thanks. I asked the question in order to reduce the number of the unknowns (and getting answers) but not adding puzzles to resolve. I.e. a person who asks usually does not have expertise to complete the puzzle – Fulproof Mar 12 '13 at 03:20
  • 1
    Updated, so you can just copy-paste and run ) – QrystaL Mar 12 '13 at 09:10
  • Shouldn't the Timer be disposed? – Amit G Mar 02 '16 at 11:56
6

Below is the code and sample harness for a cancellable Task.Delay implementation. You are likely interested in the Delay method.:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DelayImplementation
{
    class Program
    {
        static void Main(string[] args)
        {
            System.Threading.CancellationTokenSource tcs = new System.Threading.CancellationTokenSource();

            int id = 1;
            Console.WriteLine(string.Format("Starting new delay task {0}. This one will be cancelled.", id));
            Task delayTask = Delay(8000, tcs.Token);
            HandleTask(delayTask, id);

            System.Threading.Thread.Sleep(2000);
            tcs.Cancel();

            id = 2;
            System.Threading.CancellationTokenSource tcs2 = new System.Threading.CancellationTokenSource();
            Console.WriteLine(string.Format("Starting delay task {0}. This one will NOT be cancelled.", id));
            var delayTask2 = Delay(4000, tcs2.Token);
            HandleTask(delayTask2, id);

            System.Console.ReadLine();
        }

        private static void HandleTask(Task delayTask, int id)
        {
            delayTask.ContinueWith(p => Console.WriteLine(string.Format("Task {0} was cancelled.", id)), TaskContinuationOptions.OnlyOnCanceled);
            delayTask.ContinueWith(p => Console.WriteLine(string.Format("Task {0} was completed.", id)), TaskContinuationOptions.OnlyOnRanToCompletion);
        }

        static Task Delay(int delayTime, System.Threading.CancellationToken token)
        {
            TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();

            if (delayTime < 0) throw new ArgumentOutOfRangeException("Delay time cannot be under 0");

            System.Threading.Timer timer = null;
            timer = new System.Threading.Timer(p =>
            {
                timer.Dispose(); //stop the timer
                tcs.TrySetResult(null); //timer expired, attempt to move task to the completed state.
            }, null, delayTime, System.Threading.Timeout.Infinite);

            token.Register(() =>
                {
                    timer.Dispose(); //stop the timer
                    tcs.TrySetCanceled(); //attempt to mode task to canceled state
                });

            return tcs.Task;
        }
    }
}
Gusdor
  • 14,001
  • 2
  • 52
  • 64
  • Looks not good! If I'll call a lot of delay with the same CancellationToken, I'll got a lot of delegates registered on the token. – RollingStone Jun 09 '17 at 13:01
1

Extending the idea from this answer:

new AutoResetEvent(false).WaitOne(1000);
Alex R.
  • 644
  • 6
  • 9
0

You can download the Visual Studio Async CTP and use TaskEx.Delay

DVD
  • 1,744
  • 3
  • 17
  • 34
  • 5
    That's not a good idea, the CTP contains known bugs that will never be fixed. Using Bcl.Async is a much better choice. – svick Nov 15 '13 at 12:20
0

In many cases, a sheer AutoResetEvent is better than a Thread.Sleep()...

AutoResetEvent pause = new AutoResetEvent(false);
Task timeout = Task.Factory.StartNew(()=>{
pause.WaitOne(1000, true);
});

hope that it helps

f.capet
  • 39
  • 2
  • I think the original idea is to block the current thread, not to do wait on a yet another thread and just be notified later on the current thread. So, how about [this](https://stackoverflow.com/a/46087483/4955344) ? – Alex R. Sep 07 '17 at 03:49
0
    public static void DelayExecute(double delay, Action actionToExecute)
    {
        if (actionToExecute != null)
        {
            var timer = new DispatcherTimer
            {
                Interval = TimeSpan.FromMilliseconds(delay)
            };
            timer.Tick += (delegate
            {
                timer.Stop();
                actionToExecute();
            });
            timer.Start();
        }
    }
WndRnr
  • 61
  • 3
0

Here's a succinct, timer-based implementation with proper cleanup:

var wait = new TaskCompletionSource<bool>();
using (new Timer(_ => wait.SetResult(false), null, delay, Timeout.Infinite))
    await wait.Task;

To use this code on .NET 4.0, you need the Microsoft.Bcl.Async NuGet package.

Edward Brey
  • 40,302
  • 20
  • 199
  • 253
  • The OP is asking about 4.0 - there is no "await" in 4.0. – Alex R. Sep 07 '17 at 03:28
  • 1
    The `await` keyword is part of the C# language, which is separate from the .NET Framework version. The problem is that `Task` does not have `GetAwaiter`, which `await` relies on. [Microsoft.Bcl.Async](https://www.nuget.org/packages/Microsoft.Bcl.Async/) supplies this. I updated my answer to mention the NuGet package. – Edward Brey Sep 07 '17 at 11:20
  • 2
    If one to use [Microsoft.Bcl.Async](https://www.nuget.org/packages/Microsoft.Bcl.Async/), isn't `TaskEx.Delay` more succint, then? – Alex R. Sep 08 '17 at 22:36