I have a simple Windows Forms app that uses a couple BackgroundWorker elements to, well, do things in the background. While developing, I noticed I was able to do things such as get and set the .Text value of a Label in the DoWork() method, but am not able to get the value of a DrowDownList.
I was under the impression that the BackgroundWorker is not supposed to have any UI interactions, are there some exceptions to this rule?
I'm not looking for suggestions to update the UI from the BackgroundWorker, I am more curious if there are shortcuts I can take, like updating the text of a ToolStripStatusLabel directly from the BackgroundWorker.
Here is a simple example, worker 1 updates UI directly while worker 2 uses progress updates:
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void button2_Click(object sender, EventArgs e)
{
backgroundWorker2.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
toolStripStatusLabel1.Text = "Performing task 1...";
performTaskOne();
toolStripStatusLabel1.Text = "Task 1 done.";
toolStripStatusLabel1.Text = "Performing task 2...";
performTaskTwo();
toolStripStatusLabel1.Text = "Task 2 done.";
toolStripStatusLabel1.Text = "Performing task 3...";
performTaskThree();
toolStripStatusLabel1.Text = "Task 3 done.";
}
private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
{
backgroundWorker2.ReportProgress(0, "Performing task 1...");
performTaskOne();
backgroundWorker2.ReportProgress(20, "Task 1 done.");
backgroundWorker2.ReportProgress(40, "Performing task 2...");
performTaskTwo();
backgroundWorker2.ReportProgress(60, "Task 2 done.");
backgroundWorker2.ReportProgress(80, "Performing task 3...");
performTaskThree();
backgroundWorker2.ReportProgress(100, "Task 3 done.");
}
private void backgroundWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
string status = (string)e.UserState;
toolStripStatusLabel1.Text = status;
}
private void performTaskThree()
{
Thread.Sleep(1000);
}
private void performTaskTwo()
{
Thread.Sleep(1000);
}
private void performTaskOne()
{
Thread.Sleep(1000);
}
You should not be accessing or updating the UI from a background thread. Period.
It's a feature of most UI elements that they will detect if they are not being access from the UI thread and aggressively throw an exception so that the developer becomes immediately aware the first time the code runs that they are accessing the control improperly.
Not all UI controls, and not all aspects of UI controls, have this feature. Certain members are specifically documented as a member of the object that is safe to use from a non-UI thread (for example the Invoke method of a control can obviously be used from a non-UI thread; that's why it exists). For those not documented as such, you should not be using them in non-UI threads.
Just because it doesn't crash the first time you use it doesn't mean it will work. What it means is that there are likely race conditions throughout the code that can crop up depending on what other threads are accessing shared resources at the same time. It may work when you test it, it might work repeatedly, but if just the right set of circumstances take place, all sorts of bad things can happen. Perhaps the program will crash, perhaps data will be corrupted, perhaps things just won't work, or perhaps all sorts of weird and cooky things will happen. If such code gets set out to clients where you all of a sudden get a lot of different people executing the code in a lot of different and unexpected ways, as well as on very different types of environments, the chances of running into problems not encountered in your own tests goes up dramatically.
So take the time to ensure that you only access UI controls from the UI thread.
It's also worth noting, on an entirely separate note, that it greatly helps to organize your code to have your UI and presentation logic separated from your business logic; mixing them results in applications that are harder to reason about, understand, maintain, or change.
Related
I am using C#,.Net4.0 and Visual Studio 2010 and I am trying to get this behaviour from my windows form application when the user click on a button:
A GUI LED starts to blink
A long rung operation starts
When the operation at point 2 ends the LED stops blinking
private void Btn_WebService_Click(object sender, EventArgs e)
{
Thread_Blink = new Thread(() => { LED_Blink(LED.WS); });
Thread_Blink.Start();
// Do something that takes time.. Let's imulate with a sleep
Thread.Sleep(2000);
Thread_Blink.Abort();
}
I also tried using 3 different events and/or timers..
private void Btn_WebService_MouseDown(object sender, MouseEventArgs e)
{
Timer_Blink.Enabled = true;
}
private void Btn_WebService_Click(object sender, EventArgs e)
{
// Do something that takes time.. Let's imulate with a sleep
Thread.Sleep(2000);
}
private void Btn_WebService_MouseUp(object sender, MouseEventArgs e)
{
Timer_Blink.Enabled = false;
}
The result is always the same: The LED starts to blink only AT THE END of the long running operation (Thread.Sleep(2000);) and suddenly STOPS so that you can't see anything. Why does this happen and how can I get the desired behaviour?
I add further infos. I tried to use BackgroundWorked and implemented the wanted behavour in this way:
private void Btn_WebService_Click(object sender, EventArgs e)
{
BlinkWorker.RunWorkerAsync(LED.WS);
LedOn(LED.WS);
TestWebService(); // This method takes about 2 seconds to answer..
LedOff(LED.WS);
BlinkWorker.CancelAsync();
}
While the TestWebService() is running I get the LED off(LedOn(LED.WS);). When the TestWebService()has finished the LED comes on.
The BlinkWorker still does not work if started and cancelled inside the Click event.
FYI: If I make a button that starts the blinking and another button that stops it, it works perfectly.
The problem is, you're tying up the UI thread inside that Thread.Sleep. The UI thread is special - it's the only one that can make changes to the UI - so whilst it's busy sleeping, it's not able to service any attempts from your BackgroundWorker or LED_Blink timer callback to change the UI state.
So, you need to not tie up the UI thread. You need to put the Thread.Sleep code (or it's actual real equivalent) into the BackgroundWorkers DoWork handler, or use other means to avoid blocking the UI thread.
Common approaches today would be to make use of async/await if the real work you're trying to do already offers an asynchronous alternative (e.g. await Task.Delay(2000); would be the equivalent of your current Thread.Sleep(2000);). Unfortunately, using async and await would require you to move to a later version of .NET/Visual Studio - but you ought to be considering that anyway. 2010 is quite dated (and also, IMO, probably the worst one to stop on - it was notoriously slow), and .NET 4.0 (as opposed to .NET 4.5.2 or later) is no longer supported.
I would propose that you take a look at the BackgroundWorker class and the ReportProgress method.
I'm trying to use background worker to update a text label continuously, but for DoWork, if no loop used, it will only execute once, but if infinite loop is used, it freezes the GUI, any idea to solve this? Really appreciate!!! I'm pretty new to C# and still trying to learn.
Here's my code:
This in the main form:
backgroundWorkerX.DoWork += backgroundWorkerX_DoWork;
backgroundWorkerX.ProgressChanged += backgroundWorkerX_ProgressChanged;
backgroundWorkerX.WorkerReportsProgress = true;
backgroundWorkerX.RunWorkerAsync();
Then:
public void backgroundWorkerX_DoWork(object sender, DoWorkEventArgs e)
{
X = -(RSSI_PI1_ST1);
backgroundWorkerX.ReportProgress(X);
}
public void backgroundWorkerX_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label9.Text = e.ProgressPercentage.ToString();
}
public void backgroundWorkerX_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
[...] if no loop used, it will only execute once
This is the expected behavior. It will do the thing it's expected and then call RunWorkerCompleted.
[...] but if infinite loop is used, it freezes the GUI.
You don't present a loop in your question. So I'll make an assumption and will give you an educated guess that it actually consumes all the CPU, and therefore crashes the GUI.
// I assume that you loop like this somehow.
do
{
X = -(RSSI_PI1_ST1);
backgroundWorkerX.ReportProgress(X);
} while (true);
Try to add something to "slow it down" once in a while. Otherwise it will just run and run as fast as possible, and therefore consume all the CPU available. Remember that everytime you use ReportProgress it will be rendered by the GUI-thread. And this will execute very often and not leave a lot of resources for the GUI thread to respond to other things. Below I use Thread.Sleep, which will pause the thread for half a second. But remember that you will only want to do this in a background thread.
do
{
X = -(RSSI_PI1_ST1);
backgroundWorkerX.ReportProgress(X);
Thread.Sleep(500);
} while (true);
I would also consider doing this another way. If you actually have the need of pausing the thread, you might as well do it with a Timer. Thread.Sleep will lock the thread, and may therefore not be what you really want. From this answer:
Process() // method to be called after regular interval in Timer
{
// lengthy process, i.e. data fetching and processing etc.
// here comes the UI update part
Dispatcher.Invoke((Action)delegate() { /* update UI */ });
}
In my solution I got a user interface where some word automation is started by a buttonclick (lets call that button wordStart). I want to break this word automation with another buttonclick (lets call that button wordBreak).
However when I click the wordStart the user interface freezes while doing the work and it's not possible for me to click the wordBreak button.
I'm still a bit new to programming so for me this must be because the application is single threaded or atleast I could solve it with multithreading.
So this is a 2 in 1 question.
1. Is it possible to stop the execution of code with a single threaded application?
2. How do I stop the execution of code?
For question number 2 I looked a bit around the internet and found these methods which I think will work, but other suggestions are welcome:
Application.Exit
Application.Shutdown
Environment.Exit
EDIT:
As I thought this should be done with multi threading. I don't have that much experience with that so I've added this code to the question if anyone would like to help me out here. In the meantime I will look for a solution to this myself.
private void generateButton_Click(object sender, EventArgs e)
{
//Thread or backgroundworker should handle this event?
commandsChosed(); //Event to be throwed - this starts the word automation
}
private void stopButton_Click(object sender, EventArgs e)
{
//Stop/pause the working thread
}
No, by using only a single thread it gets blocked until the execution is finished. If you want to be able to cancel/pause it, you need to use another thread for the operation. For instance you can use BackgroundWorker.
Just wanted to post my answer to my own question here in case anyone had a similar problem.
As suggested by others on this question I wasn't able to implement it with a backgroundworker since it doesn't allow OLE functions like use of clipboard and such - but this is specific for what my thread is handling. A backgroundworker could definitely be useful in a lot of situations - but it can't be set to STA since it's from the threadpool.
Thread workerThread;
private void generateButton_Click(object sender, EventArgs e)
{
generateButton.Visible = false;
stopButton.Visible = true;
//Setting up a background thread
workerThread = new Thread(new ThreadStart(handleGenerateButtonClick));
workerThread.SetApartmentState(ApartmentState.STA); //In STA state the thread can use OLE functions like clipboard and handle some UI components as well.
workerThread.IsBackground = true; //It shuts down if the mainthread shuts down
workerThread.Start();
try
{
//Checking whether the currentThread is not the workerThread before blocking the currentThread until workerThread has terminated
if (Thread.CurrentThread != workerThread)
{
//Wait until workerThread has terminated
workerThread.Join();
}
//Sets the window buttons after workerThread has finished
if (!workerThread.IsAlive)
{
generateButton.Visible = true;
stopButton.Visible = false;
}
}
catch
{
}
}
private void stopButton_Click(object sender, EventArgs e)
{
generateButton.Visible = true;
stopButton.Visible = false;
//Stops the worker thread
workerThread.Abort();
}
What happens when more than one thread tries to call a form method using Invoke which updates form controls at the same time in Winforms?
static thCount = 0;
private void button1_Click(object sender, EventArgs e)
{
System.Threading.Thread t1 = new System.Threading.Thread(start);
System.Threading.Thread t2 = new System.Threading.Thread(start);
t1.Start();
t2.Start();
}
private void start()
{
System.Threading.Thread.Sleep(300);
Invoke(new MethodInvoker(guiUpdate));
}
private void guiUpdate()
{
this.label1.Text = "Updated.." + (thCount++);
this.label1.Update();
}
private void Form1_Load(object sender, EventArgs e)
{
this.label1.Text = System.Threading.Thread.CurrentThread.Name;
}
Try it out! :) You'll find that neither of them can update the UI from a background thread, instead they need to use Control.BeginInvoke to invoke work on the UI thread, in which case they will execute in the order that they call BeginInvoke.
Either of the thread will not be able to update the GUI.
You might get cross thread exception if you do not check 'InvokeRequired'.
if you still want these threads to access the same method, you can use Mutual Exclusion concept.
You can find more on Mutual Exclusion here.
This question on stack overflow also explain Mutual Exclusion in detail.
Invoke blocks until the thread has finished executing the update method.
However, this is actually only a message to the GUI thread to do this and it waits until it is done. Since the GUI thread can only execute one method at a time there is no real simultaneous execution. Nothing bad happens, but the behaviour may depend on the sequence of execution.
The sequence of execution, however, depends on which thread ever finished some guaranteed atomic (lock) operation.
I'm working in a c# windows application with vs2010 and a local database.In one of my forms i use a BindingNavigator with a group of textboxes filled by the database and a ReporViewer. I've added a background worker in order to fill the table adapters in case there are a lot of records in the database.
The problem is that the way I'm using the background worker when i debug my app i cannot see any data in the textboxes, otherwise when i run my app it's working fine. I know that this is a case of accessing the UI on a non-UI thread and it is wrong. Is there a another way around it?Thank you in advance.
Here is the code I'm using:
private void Client_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
this.clientTableAdapter.Fill(this.database1DataSet.Client);
this.projectTableAdapter.Fill(this.database1DataSet.Project);
if (InvokeRequired)
{
this.Invoke(new MethodInvoker(this.reportViewer1.RefreshReport));
return;
}
}
Typically, a background worker returns on the same thread, and would actually throw an exception about the non-UI thread. However, this might be eaten in your case. You should be using the RunWorkerCompleted event for items that are to happen after your main work is done, especially when looking to update the UI. And, this should return to the same thread it was called from (UI in your case) as mentioned above.
So, I would move your UI processing code (RefreshReport) into a new method set up for the RunWorkerCompleted.
However, my suggestion would be to take a look at the Task Parallel Library. It ends up making code much cleaner and easier to debug IMO.
Example (rough and may not compile due to the nulls, but you can get the jist :)):
var task = Task.Factory.StartNew(()=>{//Do Async Stuff});
task.ContinueWith((previousTask)=>{//Do your UI Stuff}, null, null,
TaskScheduler.FromCurrentSynchronizationContext);
//The FromCurrentSync makes sure the method returns
//to the same thread (UI in this case) that it started
I know that is not a direct answer, but more of a suggestion towards what I would consider a cleaner, more debuggable approach.