Click here to Skip to main content
Licence 
First Posted 23 May 2003
Views 134,391
Bookmarked 117 times

Proper Threading in Winforms .NET

By | 23 May 2003 | Article
Illustrates a simple example of using the Invoke capabilities of Winforms

Introduction

This is a small sample application that I built, to figure out how to "properly" do background threading in Winforms under .NET.

There is a well known rule that in the Win32 environment, a control is "owned" by the thread that created it; therefore, no other thread should (or can) safely update a control in any manner. All updates should be "posted" to the thread that owns the control. This posting actually takes place in the examples, by using the Windows Message loop - something that has existed back from Win 3.0 (perhaps earlier) days. This Message Loop still exists in .NET and forms the basis of a "Single Threaded Apartment" which VB 6 knows all too well.

All the stuff I had read was confusing, had all the methods for the work in one single class (WinForm) file. This made it difficult to see the "separation" of the form elements and where the work would be done -- and how to signal back to the caller in a separate class.

I think this is a littler easier to understand.

Walkthrough

The application's form is pretty simple:

Sample screenshot

There is a delegate declaration that identifies the callback that the worker class method will utilize, to signal to the form, what information the primary form's method needs to update, the status of the WorkerClass processing.

delegate void ShowProgressDelegate ( int totalMessages, 
                    int messagesSoFar, bool statusDone );

Within the form, the click event fires the method below:

private void button1_Click(object sender, System.EventArgs e)
{
    ShowProgressDelegate showProgress = new 
                                ShowProgressDelegate(ShowProgress);
    int imsgs = 100;
    if ( cbThreadPool.Checked )
    {
        object obj = new object[] { this, showProgress, imsgs };
        WorkerClass wc = new WorkerClass();
        bool rc = ThreadPool.QueueUserWorkItem( new WaitCallback 
                                            (wc.RunProcess), obj);
        EnableButton( ! rc );
   }
   else 
   {
        //another way.. using straight threads
        //WorkerClass wc = new WorkerClass( this, showProgress, imsgs);
        WorkerClass wc = new WorkerClass( this, 
                showProgress, new object[] { imsgs } );
        Thread t = new Thread( new ThreadStart(wc.RunProcess));
        //make them a daemon - prevent thread callback issues
        t.IsBackground = true; 
        t.Start();
        EnableButton ( false );
   } 
}

The path taken is based upon the status of the CheckBox. The path is either use ThreadPool or a ThreadStart object. With the ThreadStart object, the only way to get "state" into the WorkerClass is via the constructor. So, I've implemented WorkerClass with 2 constructors that take parameters. The first constructor is a strict 3 param constructor. The second takes the same first 2 params, but the 3rd is a parameter array, allowing more flexibility in future implementations. The sample application only uses the 3 param constructor when the check box is not enable.

Regardless, the first 2 params setup the callback target for WorkerClass.

The ShowProgress method takes a simple list of 3 params that are used to update the text box and progress bar...

private void ShowProgress ( int totalMessages, int messagesSoFar, bool done )
{
    textBox1.Text = String.Format( messagesSoFar.ToString() );
    progressBar1.Value = messagesSoFar;
    if ( done ) EnableButton ( done );
}

The WorkerClass constructor that we'll look at here is the one that takes the param list on the end:

public WorkerClass ( ContainerControl sender, 
                Delegate senderDelegate, params object[] list)
{
    m_sender = sender;
    m_senderDelegate = senderDelegate;
    m_totalMessages = (int) list.GetValue(0);
}

This just sets up internal members to designate the Form and Form's method to call when ShowProgress needs to be called.

Now, the actual work is done in LocalRunProcess using internal instance members. The public method RunProcess provide the entry point for the caller. The current thread is also forced IsBackground (make it a daemon) so if the parent thread exits, all child threads will be aborted too.

LocalRunProcess calls BeginInvoke (Invoke is the synchronous method) on the sender, passing the delegate instance that was passed in by form on WorkerClass construction. This method call initiates a background call to place a message on the message loop for the form.

private void LocalRunProcess()
{
    int i = 0;
    for ( ; i < m_totalMessages; i++)
    {
        Thread.Sleep(50);
        m_sender.BeginInvoke( m_senderDelegate, 
                new object[] { m_totalMessages, i, false } );
    }
    m_sender.BeginInvoke( m_senderDelegate, 
                new object[] { m_totalMessages, i, true } );
}
public void RunProcess()
{
    Thread.CurrentThread.IsBackground = true; //make them a daemon
    LocalRunProcess();
}

Recommended reads

A few good articles on Winforms, and Winforms over the web. You'll see the confusion in the 1st 2 articles.

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

About the Author

Shawn Cicoria

Web Developer

United States United States

Member

I have over 15 years of experience in the Application Development. Unfortunately, I still remember punch cards from College and VisiCalc on the Apple II.
 
My recent experience (about 6 years) covers the 2 main camps in distributed computing: J2EE based and COM[+] / .NET.
 
Lately, it's been deep .NET, C#, ASP.NET and the rest of the .NET Framework.
 
I've been working on Internet related technologies since 1993, initially writing Perl scripts under the original NCSA Http server.


Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
Generalvery well Pinmembervistayangxi3:37 2 Mar '12  
Questionthere's a timer class PinmemberDmityShm3:39 18 Jan '12  
GeneralThank you ! PinmemberFlorian DREVET13:35 29 Nov '11  
GeneralTerminate the form Pinmemberhefei9317:01 15 Jul '09  
GeneralRe: Terminate the form Pinmemberlovelydinasour00120:30 8 Dec '10  
QuestionHow to do more download tasks? PinmemberMissUEveryDay0:32 5 Jun '07  
JokeThanks! PinmemberStonie23:19 18 Mar '06  
QuestionThreading PinmemberProgressive Support3:24 19 Oct '05  
AnswerRe: Threading PinmemberShawn C5:06 19 Oct '05  
Do you mean the whole application or just another form inside of an application?
 
If the whole application use a mutex inside the main.
 

bool requestInitialOwnership = true;
bool mutexWasCreated;
Mutex m = new Mutex(requestInitialOwnership, "MyMutex", out mutexWasCreated);
 
if ( ! mutexWasCreated )
Application.Exit();
 
else
Application.Run(new MyForm());

 
If another form, you can traverse the window names and see if it already exists or note. This requires PInvoke
 
Or, setup another Mutex for the other forms with a system wide guaranteed unique name (MyApp:MyForm).
 
Another way is when you create the form add it to some colleciton class (Hashtable) then check the collection if it exist already.
 
Even another way is to implement the form in a singleton pattern where it has no public constructor. Just a static method called GetInstance().
 

 

 
Shawn Cicoria
shawn@cicoria.com
GeneralRe: Threading PinmemberProgressive Support5:24 19 Oct '05  
GeneralRe: Threading PinsussAnonymous13:08 19 Oct '05  
QuestionRe: Threading PinmemberProgressive Support19:54 19 Oct '05  
GeneralRe: Threading PinmemberProgressive Support0:38 20 Oct '05  
GeneralSpawning multiple threads PinmemberMartijn1:48 25 Jan '04  
GeneralRe: Spawning multiple threads PinmemberShawn Cicoria3:24 25 Jan '04  
GeneralRe: Spawning multiple threads PinmemberMartijn3:49 25 Jan '04  
GeneralRe: Spawning multiple threads PinmemberShawn Cicoria4:04 25 Jan '04  
GeneralRe: Spawning multiple threads PinmemberShawn Cicoria4:09 25 Jan '04  
GeneralRe: Spawning multiple threads PinmemberMartijn4:16 25 Jan '04  
GeneralRe: Spawning multiple threads PinmemberShawn Cicoria4:19 25 Jan '04  
GeneralNow I can see the light... PinmemberGeffl21:59 26 May '03  
GeneralRe: Now I can see the light... PinmemberShawn Cicoria2:08 27 May '03  
GeneralRe: Now I can see the light... PinmemberGeffl2:55 27 May '03  
GeneralRe: Now I can see the light... PinmemberShawn Cicoria11:30 27 May '03  
GeneralRe: Now I can see the light... PinmemberGeffl21:54 27 May '03  

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

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.5.120529.1 | Last Updated 24 May 2003
Article Copyright 2003 by Shawn Cicoria
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid