Dennis G
Dennis G

Reputation: 21778

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"?

Upvotes: 52

Views: 156727

Answers (7)

eFloh
eFloh

Reputation: 2158

(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)

Upvotes: 54

Masuri
Masuri

Reputation: 1126

You can wait UI thread the way you want it to work.

Task.Factory.StartNew(async() =>
{
    await Task.Delay(2000);

    // it only works in WPF
    Application.Current.Dispatcher.Invoke(() =>
    {
        // Do something on the UI thread.
    });
});

if you're using .Net Framework 4.5 or higher version, you can use Task.Run instead of Task.Factory.StartNew just like below.

int millisecondsDelay = 2000;

Task.Run(async() =>
{
    await Task.Delay(millisecondsDelay);

    // it only works in WPF
    Application.Current.Dispatcher.Invoke(() =>
    {
        // Do something on the UI thread.
    });
});

Upvotes: 9

macio.Jun
macio.Jun

Reputation: 9895

Have you tried

public static Task Delay(
    int millisecondsDelay
)

You can use like this:

await Task.Delay(5000);

reference: https://msdn.microsoft.com/en-us/library/hh194873(v=vs.110).aspx

Upvotes: 52

deegee
deegee

Reputation: 1623

@eFloh in the post marked as answer said:

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

That is not what the docs say.
You are using a System.Windows.Forms.Timer in your example code.
That is a Forms.Timer.
According to the C# docs the Timer events are raised on the UI thread.

This Windows timer is designed for a single-threaded environment where UI threads are used to perform processing. It requires that the user code have a UI message pump available and always operate from the same thread ...

Also see stackoverflow post here

Upvotes: 1

PVitt
PVitt

Reputation: 11740

You can start an asynchronous task that performs your action:

Task.Factory.StartNew(()=>
{
    Thread.Sleep(5000);
    form.Invoke(new Action(()=>DoSomething()));
});

[EDIT]

To pass the interval in you simply have to store it in a variable:

int interval = 5000;
Task.Factory.StartNew(()=>
{
    Thread.Sleep(interval);
    form.Invoke(new Action(()=>DoSomething()));
});

[/EDIT]

Upvotes: 14

mtijn
mtijn

Reputation: 3678

your application hangs because you are invoking the 5 second sleep/wait on the main UI thread. put the sleep/wait/whatever action in a separate thread (actually System.Windows.Forms.Timer should do that for you) and when it completes invoke the action that turns some control green. remember to check InvokeRequired. here's a short sample (SetText can be called from another thread, if it is the call will instead be invoked on the main UI thread where the textbox is on):

private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{    
    SetTextCallback d = new SetTextCallback(SetText);
    this.Invoke(d, new object[] { text });
}
else
{
    this.textBox1.Text = text;
}
}

I took the sample from here (well worth a read!).

Upvotes: 1

Tony Hopkinson
Tony Hopkinson

Reputation: 20320

You are looking at it wrong. Click the button, it kicks off a timer with an interval of x seconds. When those are up it's eventhandler executes the task.

So what don't you want to happen.

While the x seconds are elapsing.?

While The task is executing?

If for instance it's you don't want the button to be clicked until delay and task are done. Disable it in the button click handler, and enable it on task completion.

If all you want is a five second delay prior to the task, then you should pass the start delay to the task and let it take care of it.

Upvotes: 1

Related Questions