Search code examples
c#winformsformssleepwait

How to asynchronously wait for x seconds and execute something then?


I know there is Thread.Sleep and System.Windows.Forms.Timer and Monitor.Wait in C# and Windows Forms. I just can't seem to be able to figure out how to wait for X seconds and then do something else - without locking the thread.

I have a form with a button. On button click a timer shall start and wait for 5 seconds. After these 5 seconds some other control on the form is colored green. When using Thread.Sleep, the whole application would become unresponsive for 5 seconds - so how do I just "do something after 5 seconds"?


Solution

  • (transcribed from Ben as comment)

    just use System.Windows.Forms.Timer. Set the timer for 5 seconds, and handle the Tick event. When the event fires, do the thing.

    ...and disable the timer (IsEnabled=false) before doing your work in oder to suppress a second.

    The Tick event may be executed on another thread that cannot modify your gui, you can catch this:

    private System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();
    
        private void StartAsyncTimedWork()
        {
            myTimer.Interval = 5000;
            myTimer.Tick += new EventHandler(myTimer_Tick);
            myTimer.Start();
        }
    
        private void myTimer_Tick(object sender, EventArgs e)
        {
            if (this.InvokeRequired)
            {
                /* Not on UI thread, reenter there... */
                this.BeginInvoke(new EventHandler(myTimer_Tick), sender, e);
            }
            else
            {
                lock (myTimer)
                {
                    /* only work when this is no reentry while we are already working */
                    if (this.myTimer.Enabled)
                    {
                        this.myTimer.Stop();
                        this.doMyDelayedWork();
                        this.myTimer.Start(); /* optionally restart for periodic work */
                    }
                }
            }
        }
    

    Just for completeness: with async/await, one can delay execute something very easy (one shot, never repeat the invocation):

    private async Task delayedWork()
    {
        await Task.Delay(5000);
        this.doMyDelayedWork();
    }
    
    //This could be a button click event handler or the like */
    private void StartAsyncTimedWork()
    {
        Task ignoredAwaitableResult = this.delayedWork();
    }
    

    For more, see "async and await" in MSDN.


    more completeness: Depending on your Framework, there is a good chance you will have DispatcherTimer class that can handle the invocation internally (WPF-variants). (finde details in ms docs)