Wait some seconds without blocking UI execution
Asked Answered
L

10

69

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.

Larcener answered 3/3, 2014 at 21:53 Comment(3)
Can we see an example of what you mean? This is very broad.Secondly
Additionally, is there a kind of application are you building? A Console, WPF, Winforms, ASP.NET app or something else?Bernt
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?Flowerage
L
115

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");
}
Lynnelle answered 3/3, 2014 at 22:5 Comment(13)
Thanks, it seems interesting. I only have a proble, The async and await are not recognized from C#. Do you know why?Larcener
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 nuget.org/packages/Microsoft.Bcl.Async if this helps.Lynnelle
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?Larcener
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.Lynnelle
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()?Selfpossession
You don't even need PutTaskDelay() just call await Task.Delay(5000); directlyWhitver
@Whitver 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.Councilman
@Servy, I was not removing anything, I was just adding something helpful.Whitver
@Whitver 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.Councilman
@Servy, that was not my own solution. That was just an improvement of his solution.Whitver
@Whitver 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.Councilman
@Servy, ok I got youWhitver
If it wasn't "appropriate" why does the option exist?Actin
S
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();
    }
}
Saloma answered 7/3, 2016 at 23:1 Comment(5)
I have an application that's stuck at .NET 3.5 (No async/await). This was perfect.Louis
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.Bilge
Beware of the hidden evil within DoEvents. see blog.codinghorror.com/is-doevents-evil or blog.codinghorror.com/is-doevents-evil-revisitedArm
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.Whitver
This will suck the CPU.Innocence
B
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().

Bilge answered 3/3, 2017 at 19:43 Comment(2)
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.Bilge
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 bestSaloma
P
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
}));
Photoluminescence answered 3/3, 2014 at 21:59 Comment(1)
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.Photoluminescence
A
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...");
Asclepius answered 28/3, 2015 at 18:47 Comment(1)
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.Agar
R
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.

Redcoat answered 3/3, 2014 at 22:6 Comment(1)
The link could be dead one day. You should provide your example here.Actin
B
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

Breton answered 30/7, 2020 at 8:52 Comment(0)
J
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.

Journey answered 14/12, 2022 at 11:46 Comment(0)
I
0

using System.Windows.Forms.Timer

new Timer
{
    Enabled = true,
    Interval = 5000
}.Tick += (s, e) =>
{
    ((Timer)s).Enabled = false;
    
    MessageBox.Show("Hello Timer!");
};
Ignoramus answered 16/2, 2022 at 11:34 Comment(1)
Can you please add an explanation and expand your example?Hadst
I
-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.
Irreverence answered 3/3, 2014 at 22:3 Comment(4)
Can you provide a small sample?Brister
@JeroenVannevel There is an example in the link in my post. What does it lack for me to add to?Irreverence
@JeroenVannevel You should add an example so your answer still provides a value even when the link becomes invalid. Also, "look into foobar" provides only little value by itself.Theatrician
I would not flag this answer as "not an answer", but some users already did.Theatrician

© 2022 - 2024 — McMap. All rights reserved.