When performing a relatively heavy operation on your Windows Form, what often happens is that your application’s UI will freeze until the heavy operation completes. This is usually unacceptable because your application’s end user will think it crashed and probably try to close it. To solve this problem you need to run your heavy operation on a separate thread from that of your UI. This way you will be able to run your operation while also keep the end user informed of the progress from the UI. In this article I will show you how to do just that.
First let’s create a form with a textbox which displays the progress of the heavy operation, and two buttons, one to start the process and one to stop it.
Next we need to create our heavy operation method. For this example let’s just create a loop which iterates for 1 million times.
private void HeavyOperation() { // Example heavy operation for (int i = 0; i <= 999999; i++) { } }
Now let's declare our thread and a boolean
flag used to stop the heavy operation. We must use the System.Threading
namespace to access the Thread
class.
// Declare our worker thread private Thread workerThread = null; // Boolean flag used to stop the private bool stopProcess = false;
Next in the start button event handler method, we will initialise the thread and tell it to run the HeavyOperation
method.
private void btnStart_Click(object sender, EventArgs e) { this.stopProcess = false; // Initialise and start worker thread this.workerThread = new Thread(new ThreadStart(this.HeavyOperation)); this.workerThread.Start(); }
Your code should be able to compile and run but you won't see anything happening because we still have to display the heavy operation's progress on the UI.
This is where Delegates come in. In .NET a delegate is a form of type-safe function pointer. From the HeavyOperation
method which is being run under the worker thread, we cannot access the UI thread directly because it would cause a cross-thread operation exception. This is because the UI thread and our worker thread are running independently of each other and cannot access objects which have not been created by themselves.
So to write to our status textbox which is on the UI from our worker thread, we must use a delegate. At the top of our class declare a delegate and an instance of the delegate as shown below:
// Declare a delegate used to communicate with the UI thread private delegate void UpdateStatusDelegate(); private UpdateStatusDelegate updateStatusDelegate = null;
Next initialise the delegate in form load for example:
private void Form1_Load(object sender, EventArgs e) { // Initialise the delegate this.updateStatusDelegate = new UpdateStatusDelegate(this.UpdateStatus); }
As you can see in the above code, the delegate is being passed the method name UpdateStatus
. This method will be used to display activity indication to the end user. Now let's create the method:
private void UpdateStatus() { this.txtProgress.Text += "*"; }
Next, let's update our HeavyOperation
method to call the delegate, which updates the status:
private void HeavyOperation() { // Example heavy operation for (int i = 0; i <= 999999; i++) { // Check if Stop button was clicked if (!this.stopProcess) { // Show progress this.Invoke(this.updateStatusDelegate); } else { // Stop heavy operation this.workerThread.Abort(); } } }
As you can see, to call the delegate we are using the Invoke
keyword, which executes the delegate on the UI thread, since we are calling Invoke
from the this
object.
And to add the final touch to our application, we must add an event handler for the stop button and set the stopProcess
flag to true
.
private void btnStop_Click(object sender, EventArgs e) { this.stopProcess = true; }
Now if you run the application and click on your start button, you will see the status textbox filling up with the "*" character. What's happening is your worker thread is iterating for 1 million times and for each iteration a star is written to the textbox using the delegate.
I hope you liked this article. Stay tuned for more soon.
Dave
Check out my other article on worker threads – Using the BackgroundWorker Component in C# – because it has a few advantages over the method I described above. 🙂
Dave
Awesome!
(.Net newbie) Must/should you do something to guarantee updates to ‘stopProcess’ are visible across threads?
In the Java memory model, we must use ‘volatile’ keyword to 100% guarantee the JVM won’t do anything too outrageously clever with optimization that could thwart your globally-shared-state.
i like this tutorial alot. now i got a better understanding of threads. but one thing i do miss, is the complete source code, as i think its always good to see it all in all.
also, you forgot to say that you named the TextBox txtProgress, other than that AWSOME !!!
Dave:
I realize this is an old post but it is just what I need if I can make it work. This is my first thread. I wrote what I thought you said but I keep getting InvalidOperationException was unhandled. “Invoke or BeginInvoke cannot be called on a control until the window handle has been created.” This on the line “this.Invoke(this.updateStatusDelegate);” I suspect I am not doing things in the order you expected but I cannot see where the issue is. This would really help me if I could make it work. I am using MSVS C# 2008 Express and .NET 3.5. Is the full source available anywhere?
I hope your still alive and thanks for any help.
Joe
Dave:
I retract. I rewote it from scratch and now it works fine. It looks the same to me but sometimes MSVS does things in the background that I can’t see or follow.
thanks
Joe
I am having issue in this->Invoke(this->updateStatusDelegate);
I am having same issue in this->Invoke(this->updateStatusDelegate);
Thanks for this awesome tutorial. It works for me…. You are great.
Hello Dave,
Hope I meet you well.
Can this be used to run two forms that are active at the same time ?
I am producing an application that has Form Members (FM) and Form Detailed Members (FDM). The former shows basic member details and the latter shows all member’s details. FM displays a list of members while FDM shows individual members complete details. The FDM is involved by the user clicking on a member in FM. This not only starts Form Detailed Members but passes control to it, i.e the user cannot interact with FM until FDM is closed.
I want to change this behaviour so that both forms are active at the same time, which would allow the user to still be able to interact with FM while the details in FDM are refreshed when a new member is clicked in FM.
Can this and should this be done via threading ? Is there a more subtle method for this type of application.
Please note that FDM will be used to Update, Insert and delete members and hence data will be passed between both Forms in both directions.
Thanks you for your assistance.
Ayo
Loved this article, spent a few days trying to understand the minimum code required, this article sort that – cheers Dave