Click here to Skip to main content
Email Password   helpLost your password?

Sample Image - winformsthreading.jpg

Introduction

I don't intend for this article to teach multi-threaded programming in .NET, I do want to point out some important things when dealing with multi-threaded programs in a Windows Forms environment.  This article will be code light, however I have provided a full sample illustrating how to employ the practices required of a Multi-Threaded Windows Forms application.

What not to do

First and foremost (and the reason I'm writing this article) is that you absolutely, positively, cannot interact with UI elements from a thread that did not create them.  You may get away with it in some cases, but it will fail at some point and then heads will roll because you will have a lot of code to change.  Now you may ask how should you interact with the UI from another thread. 

What you should do

Luckily the designers for Windows Forms realized the plight that may be caused without being able to interact with the UI from another thread.  So they add the Invoke method to the Control class.  Invoke runs the specified delegate on the thread that create the control's window (or the parents window if it doesn't have one).

public void Invoke(System.Delegate delegate);
public void Invoke(System.Delegate delegate, object [] args);

The second form of Invoke takes an object array containing any parameters you wish to be passed into the delegate.  For those still not used to delegates, think of them as the .NET way to use function pointers.

A brief example

A simple example of using this is to create a new form, add a textbox and a button to it.  Call the textbox myTextbox.

At the top of the code file add another using statement for the threading library.

using System.Threading;

In the button's Click event place the following code.

Thread t = new Thread(new ThreadStart(SayHiThread));
t.IsBackground = true;
t.Start();

Now add this code to the form

private void SayHiThread()
{
	Invoke(new SayHiDelegate(SayHi), new object [] { "Hello from the thread" });
}
private void SayHi(string msg)
{
	myTextbox.Text = msg;
}

Outside of the form's class add this declaration

public delegate void SayHiDelegate(string msg);

Run the application and click the button, the textbox should now say "Hello from the thread".

What it did

Well that probably confused you more than it helped so I'll try to explain piece by piece what it does.

The Invoke method requires an instance of a delegate.  A delegate is nothing more than a type-safe function pointer.  You declare a delegate by creating the function signature you wish to have, then preceding the signature with the delegate keyword.  In the example above the function will take a string as its only parameter and return nothing.  You create a new delegate instance as if you were creating an instance of a class, except you pass in the name of the method you want the delegate to call.

The code inside of the button's click event creates a new thread, sets it as a background thread, then runs it.  All the thread does is run the function SayHiThread.  The thread only does one thing, it calls Invoke on the form (since SayHiThreadis a member of the form it calls the form's Invoke method).   The Invoke method takes an instance of a delegate, and an object array containing the parameters to give to the delegate.  In this case there is only one parameter; a string. 

To sum it up, Invoke calls the function which the delegate points to, passing in the parameters in the object array.

Summary

When you get down to it the hardest part of writing the code to interact with the UI from another thread is getting to understand delegates and how to use them.  As always, if there are any questions e-mail me or post them below. The source/demo show a example that's a bit more advanced, but still nothing too advanced.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Generalthis is not a really multi thread UI
E.T.
7:04 21 Dec '08  
the Invoke statement allows coders to use more than one thread in a windows form. But it DOES NOT allow more than one thread to modify the UI. All the UI changes are made by the same thread. So if the main panel is freezing for any reason, it is impossible to regain control of, for instance, the menu.

So the main problem remains: how to design a windows form application in which the UI could be splitted in regions, each region being accessed/modified by his own thread?
GeneralRe: this is not a really multi thread UI
James T. Johnson
16:36 21 Dec '08  
But that's just it, you can't! Instead you should offload the heavy processing to other threads and then do only the work required to update the UI through the Control.Invoke (or Control.BeginInvoke) call.

As long as you keep the updates to the UI quick, it will appear to the user that the UI has multiple threads operating on it.

The exception to this is that there is one method that you can call from another thread that isn't related to the Invoke functionality. If the regions you are updating are all custom drawn, you could call CreateGraphics from another thread and do the UI updates from there.

I don't know what type of problems you may run into if something tries to draw over the control such as a menu.

James

Generalcreating multithreads in C#
alzrrog
12:22 11 May '07  
hi
how can I multiply a 3x3 matrix using mutithreading(C#)and calculate the processing time for each thread , I think I will have 9 threads here.plz help,helpConfusedFrown
GeneralRe: creating multithreads in C#
Patrick Sears
12:47 11 May '07  
This is probably the wrong place to ask this question, but I'll bite:

I'll assume you know how to create a thread, or use one from the threadpool. If not, find any of the over 1000 examples on this website or type "create a thread in C#"

Do your math in the function called by that thread.

As for calculating the processing time, unless you know exactly how long each iteration will take (that is, how fast the CPU will process) you probably can't. It would be better to show progress done as a percentage of the number of iterations. Multiplying a 3x3 matrix has a fixed number of operations so you can easily count how many operations you've completed versus how many are necessarily.


Cheers,
Patrick

GeneralAvoid Invoke() - prefer BeginInvoke()
Kristof Verbiest
4:02 27 Feb '07  
Using Invoke() is prone to deadlocks, you should always try to use BeginInvoke().

I've written more about this here[^].

Better C# - kristofverbiest.blogspot.com
GeneralVery useful and easy to follow! Thanks!
anichin
7:12 17 Mar '06  
Very useful and easy to follow! Thanks!
GeneralVery Helpful
Tim Friesen
7:54 23 Aug '05  
Your article was very helpful and it got me out of a big jam.

Thanks for this useful information!

Tim Friesen

GeneralAccessing UI Element's Properties
JohnCBowman
3:10 27 Apr '04  
Thanks for the very helpful article about accessing UI elements' methods from a thread that did not create them via the Invoke method. I've got that working fine. But what about accessing the UI elements' properties? What's the correct mechanism for doing that from a thread that did not create them?

TIA,

John Bowman
Thermo Electron Corp.
Scientific Instruments Division
john.bowman@thermo.com
GeneralRe: Accessing UI Element's Properties
S. Senthil Kumar
23:27 18 May '05  
Whatever has been specified for methods is applicable to properties too, after all, internally, a property is a pair of get/set methods. The correct mechanism to access properties is the same, use Invoke/BeginInvoke.

Regards
Senthil
_____________________________
My Blog | My Articles | WinMacro
GeneralSame concept, different location
XiNull
17:38 9 Dec '03  
Hi,

Do you know if it's possible to do the same, but in a regular class. If you can execute from the background thread a delagate to run in the main thread. You example only works if you call your Invoke method from inside a class derived from a Control object. What if the class wasn't derived from anything?

Here is a quick senario that might help you understand my situation.

Let say i want to create an irc client. I want to create DLL's to handle most of my irc processes. So in my DLL, I have multiple class, and in those class i would have a thread that would simply listen to incoming Data and spit it into a Queue. From another thread, my processes takes place, handles irc commands from the queue and raises events (or delegates) for my Client guid (Forms) to handle. If i raise the event from my thread that processes the irc commands, well the code executed in my Form is still executing in the processing thread and not in the main thread of the Form.

Am i just completly on the wrong track here?

I hope you can help, thanks,

XiNull
GeneralRe: Same concept, different location
Todd Gray
8:16 21 Dec '04  
I've got the same exact problem. Has anyone solved this one?

Thanks,
Todd Gray

GeneralRe: Same concept, different location
S. Senthil Kumar
23:24 18 May '05  
You do the Invoke thing from within the event handler then. Something like
class MyForm : Form
{
public void IRCCommandReceived(object obj, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new IRCCommandReceivedDelegate(IRCCommandReceived), new object[] {obj, e});
return;
}

// Do your normal processing here.
}


Regards
Senthil
_____________________________
My Blog | My Articles | WinMacro
Generalchanging the value of a boolean when a loop is going on
pasous
12:42 6 Nov '03  
sir
my code is
public bool boolean=true;
while(boolean){console.writeline("...");}
button_onClick(..){boolean=!boolean);}
this cklick button is to change the value of the boolean to get me out of the loop.
my problem ,when the loop is going on ,i can't press the button to change the value of the boolean to put me out of loop so please give me the way i can maby use thread to solve my problem.
please give me the solution at my e_mail:
eafares265@hotmail.com
GeneralRe: changing the value of a boolean when a loop is going on
James T. Johnson
6:07 7 Nov '03  
You could use a thread for this, or if you guard yourself from re-entrency you can use Application.DoEvents().

First the thread method:

void myThread()
{
// use a local value instead of the global
// this ensures that every access to the global
// will be syncronized so only one of the threads
// can access the global at a time
bool continueLoop;

// Retreive the initial value of the global
lock(this)
{
continueLoop = boolean;
}

while( continueLoop )
{
Console.WriteLine("...");

// Pull the value from the global into our local again
//
//
lock(this)
{
continueLoop = boolean;
}
}
}

void myButton_Click(object sender, EventArgs e)
{
lock(this)
{
boolean = false;
}
}

Thread StartThread()
{
Thread thread = new Thread(
new ThreadStart(this.myThread)
);

thread.Start();
}
This is over-complicated for your example; but this over complication will become necessary if you want to do something more realistic.

The second option is to go single-threaded, but I don't recommend it unless you are a) careful and b) use it only for small tasks.

void myLoop()
{
while( boolean )
{
Console.WriteLine("...");

System.Windows.Forms.Application.DoEvents();
}
}

void myButton_Click(object sender, EventArgs e)
{
boolean = false;
}
With the second method you don't have to do much more than your current code. Application.DoEvents() just lets the form process any Windows messages that may have built up while it was working on your code. The problem is that you let all messages get processed, including ones that may start your loop again!

To help prevent this, make sure you disable any buttons, menus, etc that you don't want to run while your loop is running. Also keep in mind that your application is still single threaded, so while Application.DoEvents is processing your messages your loop is stopped.

Good luck,

James

"When you get frunk whats really fuinny is that you dont really realize you are cdtrunk till you are too drunk and by thewn you are too drunk to give a damn about being drubnk Blush " A drunk Nish over Sonork
Generalwrong code on www.ondotnet.com?
Cristian Balcanu
8:23 19 Apr '03  
In article http://www.ondotnet.com/pub/a/dotnet/2002/04/29/keys.html seems that threading is used wrong (the Thread created to manage the caret directly calls Invalidate()). Am I right?

GeneralRe: wrong code on www.ondotnet.com?
James T. Johnson
12:52 19 Apr '03  
Cristian Balcanu wrote: Am I right?
You are right, rather than do that they would be better off using a Timer (from the System.Windows.Forms namespace) to do the invalidating. That particular Timer handles the marshalling back to the control thread for you.

Another option is to P/Invoke the Win32 Caret functions so you don't even have to run a timer/thread.

James

"It is self repeating, of unknown pattern"
Data - Star Trek: The Next Generation

GeneralCannot use Invoke if calling Join inside Closing event - causes deadlock
JoergB
22:26 13 Apr '03  
When I try to wait for a thread in the closing event of the
application window then I cannot use Invoke to synchronize window
messages.

Example: The below code is an extract of a simple window application
consisting of just a button and a list box. I would like to use the
thread to update the listbox. In order to do so I use the invoke
method.
The application works fine if I do not close the application window
while the thread has not finished, i.e. there is a deadlock if the
application is inside the join and the thread calls invoke.

Any ideas what I am doing the wrong way, any work-arounds ?
Thanks in advance - Joerg.



System.Threading.Thread myThread = null;

private void Form1_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
if (myThread != null)
myThread.Join();
}

private void button1_Click(object sender, System.EventArgs e)
{
myThread = new System.Threading.Thread(new
System.Threading.ThreadStart(Test));
myThread.Start();
}

private void Test()
{
AddLine("text");
System.Threading.Thread.Sleep(1000);
AddLine("text");
myThread = null;
}

private delegate void UpdateDelegate();

private void AddLine(string strText)
{
listBox1.Items.Add(strText);
listBox1.Invoke(new UpdateDelegate(Update));
}

GeneralRe: Cannot use Invoke if calling Join inside Closing event - causes deadlock
James T. Johnson
7:14 14 Apr '03  
JoergB wrote: Any ideas what I am doing the wrong way, any work-arounds ?
The problem is that the Thread.Join method causes the current thread to wait for the specified thread to exit. In this case, the main thread waits for 'myThread' to finish.

Then you turn around and call Invoke while in 'myThread', which causes the second thread to wait until the main thread calls your delegate. But the main thread is waiting for 'myThread' to finish so it can't call the delegate.

Result: Dead-lock

I can think of a work-around, which looks rather hackish to me but it should still help.

Add another member variable to the class:
private bool isClosing = false;
Now in the form closing event:
private void Form1_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
lock (this)
{
this.isClosing = true;
} // release lock so the second thread can check the variable
if (myThread != null)
myThread.Join();
}
And in the AddLine method:
private void AddLine(string strText)
{
if (!this.isClosing)
{
lock (this)
{
// double check in case
// we were interrupted between the first if
// and the lock (such as if the main thread had locked)
if (!this.isClosing)
{
listBox1.Items.Add(strText);
listBox1.Invoke(new UpdateDelegate(Update));
}
}
}
}
This should work, though I haven't tested it.

James

"It is self repeating, of unknown pattern"
Data - Star Trek: The Next Generation

GeneralRe: Cannot use Invoke if calling Join inside Closing event - causes deadlock
JoergB
20:00 14 Apr '03  
Thank you for your immediate answer.
Your code works fine, as expected.
Actually I don't like global variables - but it seems to me that there have to be exceptions... what a pity.

Joerg

GeneralRe: Cannot use Invoke if calling Join inside Closing event - causes deadlock
James T. Johnson
20:52 14 Apr '03  
JoergB wrote: Actually I don't like global variables - but it seems to me that there have to be exceptions
I was thinking the same thing. To make it a bit prettier you can wrap the global with a read-only property, so the variable itself isn't global anymore. That would also allow the thread's method to exist outside of the form class.

James

"It is self repeating, of unknown pattern"
Data - Star Trek: The Next Generation

GeneralRe: Cannot use Invoke if calling Join inside Closing event - causes deadlock
JoergB
21:10 14 Apr '03  
Great idea, this will make it distinct from the other global varibales.
Looking at my code there is another issue to mention:
May the execution be interrupted inside the if, i.e. is there a chance that myThread is null when calling Join even if I check it?

if (myThread != null)
myThread.Join();

There was no problem so far, but maybe in a heavy environment it will be...

GeneralRe: Cannot use Invoke if calling Join inside Closing event - causes deadlock
James T. Johnson
7:11 15 Apr '03  
JoergB wrote: May the execution be interrupted inside the if, i.e. is there a chance that myThread is null when calling Join even if I check it?
Yes, it's possible; thought highly unlikely that it would happen.

To get around that you can use myThread's IsAlive property to know if the thread is still executing, just make sure you don't set myThread to null at the end of its execution Wink

James

"It is self repeating, of unknown pattern"
Data - Star Trek: The Next Generation

GeneralRe: Cannot use Invoke if calling Join inside Closing event - causes deadlock
JoergB
19:57 15 Apr '03  
James, thank you for your detailed explanations - they were of great help.
Multi-threading always is an interesting thing to play with.
Joerg

GeneralThreaded Progress Bar
jtmtv18
14:16 13 Mar '03  
im trying to make a Progress bar that can do the Increment or .Step() thousands of times a second but not lock the main thread up while doing it. im having problems though because i dont know how to setup the delegates to update the progress bar correctly. I was wondering if you would let me send you my code and see if you can help me. its short...not more then 8 K....let me know if you are willing to help..

Jesse M;

The Code Project Is Your Friend...
GeneralRe: Threaded Progress Bar
James T. Johnson
15:13 14 Mar '03  
jtmtv18 wrote: im having problems though because i dont know how to setup the delegates to update the progress bar correctly.
Setting up the delegate is pretty easy, basically it looks like a method but it has the delegate keyword at the front. Just create the delegate to look like the method's you want to call. In this case the barebone's method will look like void myMethod(), you could create a delegate for this delegage void VoidNoParameterMethod();, but instead you can use one already created in the BCL (Base Class Libraries) called MethodInvoker (in the System.Windows.Forms namespace).

Now to get Step() called on the form's thread, where myForm is the Form and myProgress is the instance of the ProgressBar control use:

myForm.Invoke( new MethodInvoker(myProgress.Step) );
Hope this answers your question. But if it is called 1000 times a second you could be spending all of your time calling Step instead of the intended response to user actions. In which case you might want to insert some Application.DoEvents() calls so that the form can empty the queue of messages waiting for it.

Good luck,

James

"It is self repeating, of unknown pattern"
Data - Star Trek: The Next Generation


Last Updated 6 Apr 2002 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010