Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / C#
Article

Threading out tasks in a C#.NET GUI

Rate me:
Please Sign up or sign in to vote.
3.00/5 (15 votes)
20 Jun 20037 min read 133.2K   1.7K   42   13
This sample demonstrates threading, based on a GUI application.

Image 1

Introduction

Let's define the problem first. This occurred to me recently when I was writing my first attempt at a (semi) "real" application in Visual C#.NET. I got my application completed (functionality wise that is) and I click on a button. The task that runs when this particular button is clicked can take a long time, depending how much text was in a particular window. Well, when its out there "doing its thing", the main window now "froze", that is, it does not repaint, move or do anything. Other controls on the form that have nothing to do with that task are also unable to be used.

That simply will not do! I need that task to run in its own thread and leave my main window alone in peace, to continue updating and allow other (unrelated) controls to be used. So, now I will present a method (there may be others..even better methods available), but this is the way I solved the problem. By the way, you may be wondering why I am writing this article...although there are many thread examples already out there, most that I found simply did something like a simple console based producer/consumer example, and it really didn't seem to show me how to solve the problem in a GUI. It seems the paradigm is (only slightly) different when trying to do the thread in a GUI. It probably seems different because in the GUI, you want to be able to do other things, as where most console examples just use threads to wait on one another or share common memory elements.

The code

Alright, let's jump right into the example problem and code. In this example, I've created a simple interface with 4 "gadgets". Just 4 things that will "do something" when activated. This example has two buttons up top with a [Do In Thread] button and a [Do Here In This Form] button. Right below that, is a RichTextBox with a large amount of text. The idea is that when [Do In Thread] is clicked, it will go out and do something that will take a little bit of time, with this large amount of text. While this is happening, I want the other "gadgets" on the form to be active as well as the form itself. So, the text processing (it reverses all the text a lot of times and redisplays it in the rich text box) will run in a separate thread (disabling the [Do In Thread] button (as well as the [Do Here In Form] button when it starts) and the main form and other controls will still be active and be able to be used, while the main task is going on in the background.

Alternatively, just to show the contrast, the same text processing function can be performed by clicking the [Do Here In This Form] button, but this time it will not go out and run the text processing in another thread, but it will do it the "standard" way, right there in the click routine for the [Do Here In This Form] button. You will notice the stark difference. As soon as you click it, nothing else can be done on the form. You cannot move the form, you can not click the other (unrelated) buttons, if you move another window in front of it, the form will not repaint, etc...

First task...and most important. Create and start a thread. See the code below that is run when the [Do In Thread]'s click handler is executed:

C#
// 
// buttonWorkInThread click handler
// 
// This method instantiates the and starts the thread.
// It also disables the button so the user doens't try to
// click it again while the thread is running.
//
private void buttonWorkInThread_Click(object sender, System.EventArgs e)
{
    // The class that has the method we'll use
    TextReverseThread oReverser; 

    // The thread object itself
    Thread oStringReverseThread; 

    // Instantiage the object
    oReverser = new TextReverseThread(this.oRTB);

    // Instantiate the thread and define the method 
    // that will be used when the thread runs
    oStringReverseThread = 
    new Thread( new ThreadStart(oReverser.DoReverse) );

    // Give the thread a name
    oStringReverseThread.Name = "STRINGREVERSE_THREAD";

    // disable start thread button
    this.buttonWorkInThread.Enabled = false;
    this.buttonWorkInThread.Text = "Working....";
    this.buttonDoHere.Enabled = false;

    // Start the thread
    oStringReverseThread.Start();        
}

Dissection:

  1. Make an instance of the class that contains the method you wish to be your thread's run(). This method is kept in another class, probably created exclusively for that thread. In this example, I have a class named TextReverseThread which is a seperate class containing the method I want to run in my thread.
  2. Create an instance of the Thread class itself. This is obviously required for a container to run the thread inside of.
  3. Instantiate the instance of the class we are using for the thread's run(). Note that I gave the local form's RichTextBox object as an argument to the constructor. We'll cover that below.
  4. Next, we have to instantiate the thread and tell it what method will be its run() method. That is, what method will it execute when he is started. We are telling it its run() method is the DoReverse() method of the just instantiated oReverser object.
  5. Before starting, we give the thread's name attribute something meaningful. This is not necessary, but if we are doing some deep thread debugging, it helps us to know exactly which thread is which, instead of seeing names like "Thread-1".
  6. Now, before we officially start it off, let's disable that [Do In Thread] button (as well as the [Do Here In This Form] button), so the user doesn't try to keep kicking it off again.
  7. Everything is in order, start the thread by calling the thread's start() method.

Now....that thing I mentioned in step 3 above was crucial for the way I designed this to work. When the thread spawns off, in order to update whatever it is supposed to update in the main form (in this case the RichTextBox), it must be able to access it to update/change it. Since this other class/thread is just created by us (and for this purpose), we have no safety issues to really worry about giving it our control (the RichTextBox). So, the constructor of the TextReverseThread class is designed to take a RichTextBox control as an argument. Now, the thread can do its work and then update the control when its work is complete.

Meanwhile.....on the main form's side of the world, we just sent off a thread to do something but now we can come right back and allow other events to take place (like redraw for example). You can see that the other things work in Figure 2 which shows the results of a few of the other gadgets that were pushed, while the thread was running. In the traditional way of just letting the click function handle the long running task, the execution is doing that task, so the events (like redraw) can't be handled... causing the main form to not redraw if you try to move it, or allow any other controls to be worked with (regardless of whether they have anything to do with the long running task or not). To see this click the [Do Here In This Form] button to see it work that way. You will notice, now, that all those things become dis-functional while its processing. Those events won't be handled until that processing is complete.

Other gotchas to worry about...

When you start that thread which is going to update a control, you better make sure NOBODY can be allowed trying to play with that control as well. You could have a user update it, then the thread returns and updates again, blowing away the data the user just entered. So, in this example, I have the RichTextBox control disabled from the beginning. However, if you wanted this to be enabled, so a user could update it, you would probably want to disable it while that thread is executing.

Another worry...is that button that kicks off a thread. Can't leave that button enabled. What if user clicked it again...no two threads are running which have access to the same control. So you better disable that button when it is clicked as well. Basically...you probably want to disable any controls that are related to what that thread is going to be "touching" (in any way) until the thread completes. You can re-enable whatever controls you had disabled in the "update event" that runs whenever your thread updates the control (in this case, the TextChanged() event of the RichTextBox control).

Let's see some example code to make that idea more clear.

private void oRTB_TextChanged(object sender, System.EventArgs e)
{
    // Re-enable "do in thread" and "Do in form"  buttons
    this.buttonWorkInThread.Text = "Do In Thread";
    this.buttonWorkInThread.Enabled = true;

    this.buttonDoHere.Text = "Do Here In This Form";
    this.buttonDoHere.Enabled = true;
}

Nothing extravagant to try list out in steps...this is the part where we are in the update handler (TextChanged() of the RichTextBox), when we are done, we can now safely re-enable those buttons which allow access to the RichTextBox.

Image 2

History

  • Date Posted: June 11, 2003

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralBad code PinPopular
thelawnet21-Feb-04 12:14
thelawnet21-Feb-04 12:14 
GeneralRe: Bad code Pin
dovydasm10-Sep-11 11:00
dovydasm10-Sep-11 11:00 
Exactly.
"Cross-thread operation not valid: Control 'oRTB' accessed from a thread other than the thread it was created on."
QuestionWhat about using the Thread Pool? Pin
Furty29-Jun-03 2:16
Furty29-Jun-03 2:16 
AnswerRe: What about using the Thread Pool? Pin
Joezer BH22-Jan-14 1:57
professionalJoezer BH22-Jan-14 1:57 
GeneralWinforms threading and Control.Invoke Pin
jmservera24-Jun-03 21:39
jmservera24-Jun-03 21:39 
GeneralThread completion delegate Pin
Los Guapos22-Jun-03 18:04
Los Guapos22-Jun-03 18:04 
GeneralRe: Thread completion delegate Pin
Anonymous23-Jun-03 2:55
Anonymous23-Jun-03 2:55 
GeneralRe: Thread completion delegate Pin
Anonymous23-Jun-03 4:08
Anonymous23-Jun-03 4:08 
GeneralRe: Thread completion delegate Pin
Los Guapos23-Jun-03 4:51
Los Guapos23-Jun-03 4:51 
GeneralApplication.DoEvents() Pin
Maximilian Hänel21-Jun-03 9:18
Maximilian Hänel21-Jun-03 9:18 
GeneralRe: Application.DoEvents() Pin
Los Guapos22-Jun-03 18:01
Los Guapos22-Jun-03 18:01 
GeneralWinform threading issue Pin
Arjan Einbu20-Jun-03 22:07
Arjan Einbu20-Jun-03 22:07 
GeneralRe: Winform threading issue Pin
2sky21-Jun-03 2:00
2sky21-Jun-03 2:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.