'Thread.Sleep not working as I would expect in C#

This code imitates the problem that I am experiencing in my app.

    private void button1_Click(object sender, EventArgs e)
    {
        button1.BackColor = Color.Red;
        Thread.Sleep(3000);
        button1.BackColor = Color.Green;
    }

I would expect this code to;

  • Make button red
  • Wait 3s
  • Make button green

but instead It is waiting 3s and then making button green. I can't just make the button green from the start as this would not work in my bigger app.

Does anyone have any idea what is wrong and also how i could fix it? Thanks in advance.



Solution 1:[1]

No. it's changing to Red but you are blocking your UI thread and thus you don't see the color changed. What if you change the handler to a async one like

private async Task button1_Click(object sender, EventArgs e)
{
    button1.BackColor = Color.Red;
    await Task.Delay(3000);
    button1.BackColor = Color.Green;
}

Solution 2:[2]

The problem is that with Sleep you are blocking the main (rendering) thread. So you set the button red, but because you are blocking the thread, the app can't render it. In fact, I expect that the whole application freeze.
I am not sure what are you using, but try to look at some timers.

EDIT or simply use tasks Delayed function calls. Just do not use threads, please.

Solution 3:[3]

The technical reason for this, is the UI works on a Message Pump (in the case of WinForms, or similar in regards to WPF). Basically a message pump is a queue of work items to do, and a while loop like the following:

while(GetMessage(&msg, NULL, 0, 0) > 0) 
{ 
  TranslateMessage(&msg); 
  DispatchMessage(&msg); 
}

What is happening, is that when you are processing your click:

  1. The click goes into the message queue
  2. Gets processed by the message pump
  3. Goes into your click method
  4. Blocks the thread for x amount of seconds, its still in your method and the pump can't process.

Why?

Thread.Sleep()

Suspends the current thread for the specified amount of time.

In short, no other messages get processed. In your example you sleep the thread before the pump has had time to process your colour change yet (its still stick processing your click).

When the messages eventually process, it goes through the backlog (your color changes being part of that), then without pause quickly changes it from one to the other.

The fix is quite simple, you need to allow your pump to process messages while you wait and as the other answers have eluded to, in modern version on .net we can use the async await pattern, and take advantage of the Task.Delay() method.

When the program process anything that is prefixed with await,

  1. If you are on the UI thread it captures the context
  2. Starts another thread with a continuation,
  3. lets the message pump continue processing.
  4. When the task has finished (and your delay is over), it returns control back to the original context (the thread you called it from).

Hey presto. Everything is as it should be.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Rahul
Solution 2 Patrik Valkovi?
Solution 3 halfer