67

I would like to wait some seconds between two instruction, but WITHOUT blocking the execution.

For example, Thread.Sleep(2000) it is not good, because it blocks execution.

The idea is that I call a method and then I wait X seconds (20 for example) listening for an event coming. At the end of the 20 seconds I should do some operation depending on what happened in the 20 seconds.

crthompson
  • 15,653
  • 6
  • 58
  • 80
user3376691
  • 789
  • 1
  • 5
  • 4
  • 2
    Can we see an example of what you mean? This is very broad. – gunr2171 Mar 03 '14 at 21:56
  • Additionally, is there a kind of application are you building? A Console, WPF, Winforms, ASP.NET app or something else? – jstromwick Mar 03 '14 at 22:05
  • Do you always want to wait X seconds for the event or can you do some operation as soon as the event comes if it comes and do some other operation if the event doesn't come within X seconds, i.e. like a timeout? – alsed42 Mar 04 '14 at 00:38

11 Answers11

113

I think what you are after is Task.Delay. This doesn't block the thread like Sleep does and it means you can do this using a single thread using the async programming model.

async Task PutTaskDelay()
{
    await Task.Delay(5000);
} 

private async void btnTaskDelay_Click(object sender, EventArgs e)
{
    await PutTaskDelay();
    MessageBox.Show("I am back");
}
Servy
  • 202,030
  • 26
  • 332
  • 449
daniellepelley
  • 1,939
  • 1
  • 11
  • 12
  • 1
    Thanks, it seems interesting. I only have a proble, The async and await are not recognized from C#. Do you know why? – user3376691 Mar 04 '14 at 16:46
  • 1
    They are fairly new additions to the .Net Framework and were added with version 4.5 on Visual Studio 2012 and 2013. There is a nuget package at http://www.nuget.org/packages/Microsoft.Bcl.Async/ if this helps. – daniellepelley Mar 04 '14 at 20:37
  • Thank you. I installed Visual Studio Express 2013 and it has async and await now. But when want to use the method Delay of Task, it does not exist. The only methods allowed are Equals, RefernceEquals, WaitAll and WaitAny... Do you know why? – user3376691 Mar 05 '14 at 13:56
  • Task.Delay(10).Wait() will work but it will block the execution of the thread, ie the user interface will become unresponsive. Unless that is the behaviour you want I would use the async pattern. – daniellepelley Feb 24 '15 at 09:09
  • Is it neccesary for `btnTaskDelay_Click` to be async and use `await` for `PutTaskDelay()`? Would it not suffice to only await the `Task.Delay()` inside `PutTaskDelay()`? – Flater Jan 24 '17 at 10:53
  • 9
    You don't even need `PutTaskDelay()` just call `await Task.Delay(5000);` directly – Black Aug 15 '17 at 13:27
  • 2
    @Black It's not appropriate to make such changes to other people's answers. Commenting is fine, but if someone else prefers to extract the code out into a method, that's their decision to make. – Servy Aug 15 '17 at 13:31
  • 1
    @Servy, I was not removing anything, I was just adding something helpful. – Black Aug 15 '17 at 14:39
  • 1
    @Black And that's not appropriate in an edit. If you have your own solution to add, you can post your own answer, but adding your own solution into someone else's answer is not appropriate. – Servy Aug 15 '17 at 14:40
  • 1
    @Servy, that was not my own solution. That was just an improvement of his solution. – Black Aug 15 '17 at 15:14
  • 6
    @Black It was *your* improved version. The point stands. If you come up with a way for a solution to be improved, either post a comment for the author to decide to make the change, or post your alternative as your own answer (citing a derived work as appropriate). Editing your new version into someone else's answer isn't appropriate. – Servy Aug 15 '17 at 15:16
  • 2
    @Servy, ok I got you – Black Aug 15 '17 at 16:23
  • If it wasn't "appropriate" why does the option exist? – The incredible Jan Sep 29 '22 at 06:07
29

I use:

private void WaitNSeconds(int segundos)
{
    if (segundos < 1) return;
    DateTime _desired = DateTime.Now.AddSeconds(segundos);
    while (DateTime.Now < _desired) {
         System.Windows.Forms.Application.DoEvents();
    }
}
Servy
  • 202,030
  • 26
  • 332
  • 449
Omar
  • 315
  • 3
  • 3
  • 1
    I have an application that's stuck at .NET 3.5 (No async/await). This was perfect. – Michael Oct 13 '16 at 18:08
  • This solution is effective if you cannot upgrade to .NET 4.5, but it will consume excessive CPU resources without a minor tweak. See my answer for a detailed explanation. – Nick Painter Mar 03 '17 at 20:23
  • 3
    Beware of the hidden evil within DoEvents. see https://blog.codinghorror.com/is-doevents-evil/ or https://blog.codinghorror.com/is-doevents-evil-revisited/ – Rubidium 37 Jul 18 '17 at 07:22
  • Watch out! If you use this wait in the load event handler of the form, then your form will only show after the wait time passed. – Black Aug 15 '17 at 12:54
  • 2
    This will suck the CPU. – Gentleman Jul 05 '18 at 10:34
15

Omar's solution is decent* if you cannot upgrade your environment to .NET 4.5 in order to gain access to the async and await APIs. That said, there here is one important change that should be made in order to avoid poor performance. A slight delay should be added between calls to Application.DoEvents() in order to prevent excessive CPU usage. By adding

Thread.Sleep(1);

before the call to Application.DoEvents(), you can add such a delay (1 millisecond) and prevent the application from using all of the cpu cycles available to it.

private void WaitNSeconds(int seconds)
{
    if (seconds < 1) return;
    DateTime _desired = DateTime.Now.AddSeconds(seconds);
    while (DateTime.Now < _desired) {
         Thread.Sleep(1);
         System.Windows.Forms.Application.DoEvents();
    }
}

*See https://blog.codinghorror.com/is-doevents-evil/ for a more detailed discussion on the potential pitfalls of using Application.DoEvents().

Nick Painter
  • 720
  • 10
  • 13
  • 1
    At the time that I posted this solution, I did not meet the minimum reputation needed to add it as a comment to Omar's solution. For now I am keeping this answer open since it involves formatted code. – Nick Painter Mar 03 '17 at 20:21
  • 2
    I use to program MVVM applications since Fw 3.5 to 4.5.1 and even with all new resources sometimes simple solutions to a problem are the best – Omar Mar 06 '17 at 16:27
14

This is a good case for using another thread:

// Call some method
this.Method();

Task.Factory.StartNew(() =>
{
    Thread.Sleep(20000);

    // Do things here.
    // NOTE: You may need to invoke this to your main thread depending on what you're doing
});

The above code expects .NET 4.0 or above, otherwise try:

ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
{
    Thread.Sleep(20000);

    // Do things here
}));
Matt
  • 2,682
  • 1
  • 17
  • 24
  • 5
    I'm just going to add a comment to my own older answer here to say that I prefer the async methods mentioned in other answers as they're both cleaner and better practice. If you haven't already read up on async, I highly recommend doing so. – Matt Dec 06 '15 at 04:25
3

If you do not want to block things and also not want to use multi threading, here is the solution for you: https://msdn.microsoft.com/en-us/library/system.timers.timer(v=vs.110).aspx

The UI Thread is not blocked and the timer waits for 2 seconds before doing something.

Here is the code coming from the link above:

        // Create a timer with a two second interval.
    aTimer = new System.Timers.Timer(2000);
    // Hook up the Elapsed event for the timer. 
    aTimer.Elapsed += OnTimedEvent;
    aTimer.Enabled = true;

    Console.WriteLine("Press the Enter key to exit the program... ");
    Console.ReadLine();
    Console.WriteLine("Terminating the application...");
Daan
  • 2,478
  • 3
  • 36
  • 76
  • Long time ago BUT I have an asp.net page that handle POSTs mainly backend stuff that needed a timer and this one did the job no blocking. – becker Aug 17 '18 at 16:21
1

i really disadvise you against using Thread.Sleep(2000), because of a several reasons (a few are described here), but most of all because its not useful when it comes to debugging/testing.

I recommend to use a C# Timer instead of Thread.Sleep(). Timers let you perform methods frequently (if necessary) AND are much easiert to use in testing! There's a very nice example of how to use a timer right behind the hyperlink - just put your logic "what happens after 2 seconds" right into the Timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); method.

Community
  • 1
  • 1
Matthias R.
  • 441
  • 2
  • 15
1

hi this is my suggestion

 .......
var t = Task.Run(async () => await Task.Delay(TimeSpan.FromSeconds(Consts.FiveHundred)).ConfigureAwait(false));
                //just to wait task is done
                t.Wait();

keep in mind put the "wait" otherwise the Delay run without affect application

luka
  • 605
  • 8
  • 15
1

I see that many colleagues are suggesting using a timer. Please don't, but if you decide to use a timer, make sure you unsubscribe from the Elapsed event and Dispose it, otherwise it keeps your class in memory until you restart your application.

You can also use this approach in C#:

// Perform some logic here. Executes first.

Task.Delay(20000).ContinueWith(t =>
{

   // Continue here after 20 seconds with other logic back in the UI thread.
   // Executes third.
             
}, TaskScheduler.FromCurrentSynchronizationContext());

// Some more logic here. Executes second.

This is just a simple example, this logic must be updated if the scenario is more complex.

Alexandru Dicu
  • 1,151
  • 1
  • 16
  • 24
0

using System.Windows.Forms.Timer

new Timer
{
    Enabled = true,
    Interval = 5000
}.Tick += (s, e) =>
{
    ((Timer)s).Enabled = false;
    
    MessageBox.Show("Hello Timer!");
};
-2

If possible, the preferred approach should be using an asynchronous way or a second thread.

If this isn't possible or wanted, using any implementation of DoEvents() is a bad idea, since it may cause problems Possible problems with DoEvents. This is mostly about DoEvents with Winforms but the possible pitfalls in WPF are the same.

Then putting a frame on the Dispatcher with the wanted sleep time can be used:

using System;
using System.Threading;
using System.Windows.Threading;

public static void NonBlockingSleep(int timeInMilliseconds)
{
    DispatcherFrame df = new DispatcherFrame();

    new Thread((ThreadStart)(() =>
    {
        Thread.Sleep(TimeSpan.FromMilliseconds(timeInMilliseconds));
        df.Continue = false;

    })).Start();

    Dispatcher.PushFrame(df);
}
marsh-wiggle
  • 2,508
  • 3
  • 35
  • 52
-4

Look into System.Threading.Timer class. I think this is what you're looking for.

The code example on MSDN seems to show this class doing very similar to what you're trying to do (check status after certain time).

The mentioned code example from the MSDN link:

using System;
using System.Threading;

class TimerExample
{
    static void Main()
    {
        // Create an AutoResetEvent to signal the timeout threshold in the
        // timer callback has been reached.
        var autoEvent = new AutoResetEvent(false);
        
        var statusChecker = new StatusChecker(10);

        // Create a timer that invokes CheckStatus after one second, 
        // and every 1/4 second thereafter.
        Console.WriteLine("{0:h:mm:ss.fff} Creating timer.\n", 
                        DateTime.Now);
        var stateTimer = new Timer(statusChecker.CheckStatus, 
                                autoEvent, 1000, 250);

        // When autoEvent signals, change the period to every half second.
        autoEvent.WaitOne();
        stateTimer.Change(0, 500);
        Console.WriteLine("\nChanging period to .5 seconds.\n");

        // When autoEvent signals the second time, dispose of the timer.
        autoEvent.WaitOne();
        stateTimer.Dispose();
        Console.WriteLine("\nDestroying timer.");
    }
}

class StatusChecker
{
    private int invokeCount;
    private int  maxCount;

    public StatusChecker(int count)
    {
        invokeCount  = 0;
        maxCount = count;
    }

    // This method is called by the timer delegate.
    public void CheckStatus(Object stateInfo)
    {
        AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;
        Console.WriteLine("{0} Checking status {1,2}.", 
            DateTime.Now.ToString("h:mm:ss.fff"), 
            (++invokeCount).ToString());

        if(invokeCount == maxCount)
        {
            // Reset the counter and signal the waiting thread.
            invokeCount = 0;
            autoEvent.Set();
        }
    }
}
// The example displays output like the following:
//       11:59:54.202 Creating timer.
//       
//       11:59:55.217 Checking status  1.
//       11:59:55.466 Checking status  2.
//       11:59:55.716 Checking status  3.
//       11:59:55.968 Checking status  4.
//       11:59:56.218 Checking status  5.
//       11:59:56.470 Checking status  6.
//       11:59:56.722 Checking status  7.
//       11:59:56.972 Checking status  8.
//       11:59:57.223 Checking status  9.
//       11:59:57.473 Checking status 10.
//       
//       Changing period to .5 seconds.
//       
//       11:59:57.474 Checking status  1.
//       11:59:57.976 Checking status  2.
//       11:59:58.476 Checking status  3.
//       11:59:58.977 Checking status  4.
//       11:59:59.477 Checking status  5.
//       11:59:59.977 Checking status  6.
//       12:00:00.478 Checking status  7.
//       12:00:00.980 Checking status  8.
//       12:00:01.481 Checking status  9.
//       12:00:01.981 Checking status 10.
//       
//       Destroying timer.
marsh-wiggle
  • 2,508
  • 3
  • 35
  • 52
LB2
  • 4,802
  • 19
  • 35