Thread.Sleep not working as I would expect in C# - 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.

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;
}

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.

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:
The click goes into the message queue
Gets processed by the message pump
Goes into your click method
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,
If you are on the UI thread it captures the context
Starts another thread with a continuation,
lets the message pump continue processing.
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.

Related

sleep not behaving as expected [duplicate]

This question already has answers here:
Implementing a pause in WPF
(3 answers)
Closed 9 years ago.
I have a WPF app and I want to sleep for a second before displaying certain info on screen. So its.
1. Click a button.
Sleep 1 sec.
Display a label.
Sleep 1 sec.
Display a label.
etc etc
private void RunTest()
{
clearTable();
System.Threading.Thread.Sleep(1000);
this.Cursor = Cursors.Wait;
Label_OS_Result.Content = operatingSystem;
Image_OS.Visibility = Visibility.Visible;
System.Threading.Thread.Sleep(1000);
Label_CPU_Result.Content = procName;
Image_CPU.Visibility = Visibility.Visible;
System.Threading.Thread.Sleep(1000);
}
But when I click my button it seems to wait for a few seconds and then displays everything all at once.
Whats going on here??
It is working as it is expected.
Thread.Sleep stops the execution of the current thread for specified amount of time. So, when you change the visibility of an image, it will make further calls to change the visibility in the framework, then it will try to draw it on screen (invalidates). When you sleep the thread before this operation finishes it will not have enough time to finish this operation. You are simply blocking the UI thread responsible of drawing something on the screen.
Beware that Thread.Sleep may not be the solution, it is better for you to have a look at DispatcherTimer or BackgroundWorkers.
You don't want to Thead.Sleep() on the thread displaying your GUI.
Instead, you should try await Task.Delay(). This is similar to sleep, but it won't block the GUI thread.
This will allow all the rendering to still happen, while delaying the visiblity changes for your labels.
It is working as intended.
It may not be what you expected, but the problem here is your expectations.
What happens when you fiddle with properties on most visual controls, that should end up changing the appearance of the control, is first to make the internal adjustments to the controls properties (ie. data), and then trigger a redraw. To trigger the redraw, a message is usually posted to the message queue of the thread that owns this visual control.
This thread usually processes messages one at a time, be it a button click, mouse movement, or the paint messages that was just posted because you changed the image.
However, that thread is basically written like this (pseudo-code):
while (true)
{
var message = GetNextMessageFromQueue();
ProcessMessage(message);
}
The last time this loop was active, it started processing a message that ended up calling your code, the one that is now sleeping.
So basically, to get the message pump (the typical name of the code that implements this loop) to continue pumping messages, and thus process your paint message, is not to sleep.
You might want to separate your processing out to a different thread, or some other asynchronous way of doing things "in the background".
Note that you will get the same problem if you start doing any kind of lengthy processing in response to a button click or similar, without doing it on a background thread (or again, some other asynchronous way). The GUI will appear to have frozen.
This problem, however, is so common that Microsoft ended up building in special support for it in Windows. It used to be that a window just seemed to hang. Now, however, if the message queue "fills up", Windows will fade out the windows and tack on a caption to it indicating that it has stopped responding. Not a solution, but slightly better.
Sleep function is behaving correct.
Better you use a Timer to do so,
// Declare it in the constructor of the class
int level = 0;
Timer timer = new Timer
timer.Interval = 1000;
timer.Tick +=new EventHandler(timer_Tick);
private void timer_Tick(object sender, EventArgs e)
{
timer.Stop();
if (level == 0)
{
Label_OS_Result.Content = operatingSystem;
Image_OS.Visibility = Visibility.Visible;
++level;
}
else if (level == 1)
{
Label_CPU_Result.Content = procName;
Image_CPU.Visibility = Visibility.Visible;
++level;
}
else if (level > 1)
{
this.Cursor = Cursors.Default;
return;
}
timer.Start();
}
private void RunTest()
{
clearTable();
//System.Threading.Thread.Sleep(1000);
timer.Start();
this.Cursor = Cursors.Wait;
}

c# spawning process after Form Shown

When my main form loads I want to do some processing like loading the last client viewed. This extra processing causing the drawing of the form to be pretty ugly. I am doing it on form shown so I would expect that the form would be done painting.
I have used PostMessage... Is there a modern day version of PostMessage? During this processing I would like to set the hourglass cursor.
I think what you are trying to do is background processing. So you can use a BackgroundWorker class. When your form loads, you would start the background worker and when the worker is finished, it will notify your form through the RunWorkerCompleted event. You can even get progress updates from the worker by implementing ProgressChanged event.
How about using Task + ContinueWith. (If you upgrade to .Net 4.5 async/await would be a better choice).
private void Form_Load(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(5000); //Some long Running Jobs
return "Finished";
})
.ContinueWith(t =>
{
this.Text = t.Result; //Update GUI
},
TaskScheduler.FromCurrentSynchronizationContext());
}
I am not sure why would you want to use PostMessage. If you post a message using PostMessage it will eventually be obtained by GetMessage and unless the message is posted from a different thread whole exercise seems a little futile. Am I missing something here? As for BackgroundWorker please refer to the solution for Label is not set until method is done.

c# winforms events

In my c# winform app I have a button which when clicked, it performs some calculations which can tke some time.
I also have a label.visible = false which I would like to label.visible = true right after the button click so that the user can see the app working away.
The thing is even when label.visible = true is the first thing in the button1_Click(object sender, EventArgs e) method, it only toggles to visible at the very end, after the calculation is performed.
How can I show the label right after the button_click?
If you need your application to remain responsive whilst your app is processing, take a look at Background Worker
You can force your form to update & process any background messages by calling:
Application.DoEvents();
Just after you have changed your label - although this is probably a bit of a hacky solution.
Your calculation is being performed on the UI thread. This means that it is blocking the UI from refresshing after you have set the visibility of the label.
You should consider doing the calculation on another thread using a Task. This will allow the UI to be responsive during what it seems is a long operation that can run in the background.
e.g.
var taskCalc = Task.Factory.StartNew(() => //Do Calculation );
There are several ways to tackle this.
One way (the simplest) is to call
this.Refresh();
right after you set the Label to visible.
Another is to do your calculations in a background thread. The easiest way to do that is to use a BackgroundWorker. Then your main thread can just continue serving the UI (refreshing the form, responding to buttons etc.) while the background worker thread performs the computation.
See http://www.dotnetperls.com/backgroundworker for more on background worker threads.
Like others have said: You are performing your work on the UI Thread. I am showing you another valid way of achieving what you need. You can move your work to a separate thread using an anonymous delegate.
(new System.Threading.Thread(() =>
{
dowork(); // What ever work you need put here.
})).Start();

Start long process after modal dialog popup

I have a modal dialog with a cancel button only which pops up when the user clicks on a button. Aftre the modal dialog pops up, I would like to start a long process which monitors external event. If the event happens, then the dialog will be closed automatically. The user can cancel the monitoring process by clicking the cancel button.
I assigned the process start to the Shown event
private void ProceedForm_Shown(object sender, System.EventArgs e)
{
controller.StartSwiping();
}
The process itself is a loop
public void StartSwiping()
{
Status status;
do
{
status = CallForFeedback();
} while (status == Status.Pending);
form.DialogResult = DialogResult.OK;
form.Close();
}
The process starts fine, but the dialog does not pop up, so the user can non cancel the process. I also tried to assign the start to the Load event, but nothing changed.
Is there any way to Show the dialog and after that start the process?
Thanks
Your problem is that you are doing everything in the UI thread. You need to put you status monitoring loop in a separate thread so that the UI thread can remain responsive.
There are several ways you can do this, but one easy place to start is with the BackgroundWorker class
Use a Task to do your LongRunning events:
CancellationTokenSource _cancelationTokenSource = new CancellationTokenSource();
new Task(() =>
{
//Do LongRunning task
}, _cancelationTokenSource.Token, TaskCreationOptions.LongRunning).Start();
Use the _cancelationTokenSource to cancel the task when needed.
I would move the long running code onto a background thread as you are blocking the UI thread, which is why the UI never displays.
Use a background worker class for the controller functionality http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
When the work is completed on the background worker (i.e. the event is received) then you can use the following mechanism to callback onto the UI thread:
http://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx
Note: the article says you can turn off the crossthreadexception this would be considered bad practice, instead handle it the correct way using the InvokeRequired check and then invoke method on the windows form.
Others have suggested using a BackgroundWorker, or some other sort of background thread. While in many cases this is appropriate here, there is likely an even better solution. You're not just doing some long running task, you're waiting for something to happen. Rather than constantly polling...whatever it is, you should be using events. There should be an event that is triggered when you are done, and you should subscribe to that event to do whatever you need to do (i.e. close the dialog) when the correct conditions are met.

Winforms C# - Show Cancel dialog when main UI thread is busy

I have a main form that contains an edit control that occupies the entire form. There is another worker thread that constantly writes log messages to this edit control. Now I want to show a dialog box with just a cancel button while the main UI's edit control is displaying stuff. The problem is that the cancel dialog is non-responsive while the updates are happening behind it and I cannot click on the cancel button. Any idea on how to resolve it. I was thinking of creating another UI thread and show the cancel button from it. Any other alternatives?
EDIT#1
I should clarify that I already use a worker thread to do the work.
DisplayLogs() is in a seperate thread.
DisplayLogs() is called from other threads.
LogMessage is the delegate that points to the method UpdateMessage in main UI.
The control used is a TextBox. I have tried other controls like listview,
richtextboxsand, etc. still the same result.
Worker Thread
void DisplayLogs()
{
lock (this)
{
while (logQueue.Count > 0)
{
string logMessagemessage = logQueue.Dequeue();
LogMessage(string.Concat(logMessagemessage, Environment.NewLine));
}
}
}
Main UI
public void UpdateMessage( string message)
{
if (!txtLog.IsHandleCreated)
{
return;
}
if (txtLog.InvokeRequired)
txtLog.BeginInvoke( new UpdateLogDelegate( UpdateLog), message);
else
txtLog.AppendText(message);
}
The main solution is to offload the expensive code onto a background worker and leave your UI thread responsive for UI actions. Your form can then simply show a modal dialog or something.
MSDN - How to use a Background Worker
In this situation it's necessary to move the majority of the work to a new thread, and clear up the UI thread for cancel messages etc.
You are going about this backwards. The main thread should, in theory, always be available to accept user input. Anything that may block for extended periods of time (heavy computation, database access, network access) should be done in a background thread. The idea is to have the edit control's data being computed and populated by a background thread (BackgroundWorker objects work nicely here) so that the main thread is always available if the user clicks on the cancel button.
Your problem is that the your UI thread is ALWAYS busy. I am saying this assuming that the number of items in logQueue is quite large. The while loop doesn't quit till the queue is empty. So it keeps hitting the UI thread with request for updates.
Also the if (txtLog.InvokeRequired) is kind of pointless because you are always calling the method from a worker thread.
So, since a .net WinForm application has only a single UI, which in your case is too busy to process other notifications, the new window appears stuck (because the paint messages are stuck in the message queue and cannot be processed as it is already flooded with the text box update messages)
You could stick an Application.DoEvents inside your loop which will give the message loop some time to process the pending notifications. However this is kind of a hack, in my opinion, as the UI behavior is sometimes not predictable. It may lead to things like stuttering while moving a dialog, delayed responses to click events etc.
Another point, MessageBox.Show or a Form.ShowDialog (if this is what you are using for the cancel button) is a blocking call. The thread on which you show it WILL hang till you dismiss the dialog. Try Form.Show and set the parent property to the main form.
Another alternative is to add a timer and process only X notifications per Y seconds. This will give the UI thread some breathing room for performing other activities.

Categories