|
|
Comments and Discussions
|
|
 |

|
Saved me hours...
SoftechSoftware
Davide Calabro'
davide_calabro@yahoo.com
http://www.softechsoftware.it
|
|
|
|

|
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?
|
|
|
|

|
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
|
|
|
|
|

|
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.
|
|
|
|
|

|
Very useful and easy to follow! Thanks!
|
|
|
|

|
Your article was very helpful and it got me out of a big jam.
Thanks for this useful information!
Tim Friesen
|
|
|
|

|
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
|
|
|
|

|
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
|
|
|
|

|
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
|
|
|
|

|
I've got the same exact problem. Has anyone solved this one?
Thanks,
Todd Gray
|
|
|
|

|
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;
}
}
Regards
Senthil
_____________________________
My Blog | My Articles | WinMacro
|
|
|
|

|
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
|
|
|
|

|
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()
{
bool continueLoop;
lock(this)
{
continueLoop = boolean;
}
while( continueLoop )
{
Console.WriteLine("...");
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 "
A drunk Nish over Sonork
|
|
|
|
|

|
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
|
|
|
|

|
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));
}
|
|
|
|

|
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;
} if (myThread != null)
myThread.Join();
}And in the AddLine method:
private void AddLine(string strText)
{
if (!this.isClosing)
{
lock (this)
{
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
|
|
|
|

|
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
|
|
|
|

|
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
|
|
|
|

|
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...
|
|
|
|

|
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
James
"It is self repeating, of unknown pattern"
Data - Star Trek: The Next Generation
|
|
|
|

|
James, thank you for your detailed explanations - they were of great help.
Multi-threading always is an interesting thing to play with.
Joerg
|
|
|
|

|
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...
|
|
|
|

|
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
|
|
|
|

|
where did you go to school for programming james? im currently in school getting my mcsd....where would you recommend going after that.. ?
Jesse M
P.s : i only know C#...but iam intrested in learning c++ also.
The Code Project Is Your Friend...
|
|
|
|

|
Doh, I was meaning to reply to this but never got around to it.
jtmtv18 wrote:
where did you go to school for programming james?
I'm a self-taught programmer, but I was enrolled in the Computer Science curriculum at Michigan Tech for 2.5 years. Unfortunately it took 2.5 years for me to realize that a CS degree wasn't going to help me.
Now I'm considering if I should pursue a degree I can use, like Business or Accounting.
jtmtv18 wrote:
im currently in school getting my mcsd....where would you recommend going after that.. ?
Given my experience I probably can't give you a good answer. Knowing about the job market in your area would help also. I've heard a lot of places in the US don't care about certifications, but yet others have said that they are almost a requirement for getting a job in other places.
jtmtv18 wrote:
i only know C#...but iam intrested in learning c++ also.
Again I was self-taught and unfortunately my learning materials were so old I was taught that templates were buggy and slow. I imagine most of the C++ books out there now are better about this.
I can't give any names or authors becuase they escape my memory right now, but a quick question asking for them in the Visual C++ forum could give you a ton of choices. You could also look up various titles on Amazon and read the reviews to see which ones look like they could help you.
Good luck
James
"It is self repeating, of unknown pattern"
Data - Star Trek: The Next Generation
|
|
|
|

|
Hey,
where did you get this trillian skin for windows from? It machtes exactly the default trillian skin?
|
|
|
|

|
Wonder why I don't remember seeing this message before....
At the bottom of the article (in the 'About James..' section) is a link to the Visual Style I use in WindowsXP.
James
"It is self repeating, of unknown pattern"
Data - Star Trek: The Next Generation
|
|
|
|

|
Hi fellows,
this is my first post. Hello to all CodeProject community.
Keep up the very good work...
Back to question...
I was trying to make MDI app where each form would run in its own thread.
(Somehow I feel this is not possible, but anyway). I managed to bring up
the MDI form in separate thread, but I can't see any difference i.e.
if form is waiting for some event, I have no response from main window either.
Is this because whole GDI is runnig in separate thread ?
Should I change approach ?
Thanks,
Davorin
|
|
|
|

|
Davorin P. A. wrote:
I was trying to make MDI app where each form would run in its own thread.
Not sure if that is possible; because the handle to each of the child windows would need to be valid on the parent thread. The behavior you see sounds like it created the windows on the same thread.
The preferred way of accomplishing your goal would be to keep the GUI stuff all in one thread; then when you need to do work, use the ThreadPool (create a delegate to the method doing the work and call BeginInvoke on the delegate) or create your own thread to do the work. Refer to MSDN for differences between the ThreadPool and creating your own thread.
James
"And we are all men; apart from the females." - Colin Davies
|
|
|
|

|
Yeap, you're probably right. I will try suggested approach.
Thanx for your time and info James.
Cheers,
Davorin
|
|
|
|

|
The article was good, but I am a little confused with the concept. Why can't the thread simply call the function directly, without using a delegate? Or does it just look that way because the thread is a simple one, rather than a seperate class.
--
David Wengier
Sonork ID: 100.14177 - Ch00k
|
|
|
|

|
You can't call the function from the background thread because it didn't create the UI objects. I actually don't know why you can't do that, either the explaination was far too complicated for me to understand at the time or no one has provided a good one. [Edit: strike my edit, i was stupid when I looked it up ]
Here is part of what the documentation says for Control.Invoke
Note There are four methods on a control that are safe to call from any thread: Invoke, BeginInvoke, EndInvoke, and CreateGraphics. For all other method calls, you should use one of the invoke methods to marshal the call to the control's thread.
BeginInvoke and EndInvoke are used for asynchronous calling of the Invoke method (ie the thread continues running while the invoke call is actually made). I didn't cover that aspect of it because I wanted to keep the examples simple.
Nish has covered the basics of Begin/End Invoke in his Beginners Remoting article.
Hope that answers your question, at first I took it to mean something else so let me know if I misinterpreted it.
James
Sonork: Hasaki
"I left there in the morning
with their God tucked underneath my arm
their half-assed smiles and the book of rules.
So I asked this God a question
and by way of firm reply,
He said - I'm not the kind you have to wind up on Sundays."
"Wind Up" from Aqualung, Jethro Tull 1971
|
|
|
|

|
James T. Johnson wrote:
You can't call the function from the background thread because it didn't create the UI objects.
Even in normal win32 programming you are advised not to do that. If you have a thread that needs to access a UI element that was created in a separate thread, you are supposed to send/post a message to the UI thread and allow the UI thread to access the UI element. This is even more true with MFC than in plain win32, primarily because MFC is not exactly thread safe. Often people make a mistake of starting a thread when they have progress baras. The mistake is not in starting the thread, but they simply forget that the progress bar was not created by that thread and access the progress bar randomly from the new thread. They soon run into access violations.
Nish
If I am awake and my eyes are closed, it does not necessarily mean that I am thinking of naked women.
|
|
|
|

|
doh! I just realized that in my edit that the part I looked in his book only dealt with GDI access, not child windows.
Time to strike that from my edit
James
Sonork: Hasaki
"I left there in the morning
with their God tucked underneath my arm
their half-assed smiles and the book of rules.
So I asked this God a question
and by way of firm reply,
He said - I'm not the kind you have to wind up on Sundays."
"Wind Up" from Aqualung, Jethro Tull 1971
|
|
|
|

|
Nish [BusterBoy] wrote:
Even in normal win32 programming you are advised not to do that.
I can't see why there should be any problem with this as long as you perform any required synchronization.
Nish [BusterBoy] wrote:
This is even more true with MFC than in plain win32, primarily because MFC is not exactly thread safe.
This isn't an issue for most code. MFC's however is a nightmare to try and do multi-threaded GUI development with. The main problem is that MFC's handle map's are maintained on a per thread basis. This means if you have a CWnd* in one thread you can't use it another. Instead you have to do double back-flips to get things to work. This causes me no end of problems in coding ED, and if I'd known how difficult MFC made this I probably wouldn't have used it.
Neville Franks, Author of ED for Windows. www.getsoft.com
|
|
|
|

|
The reason why you cant access a control from a thread that did not create it is simple. When a thread creates a control it places the HANDLE of the control in its Handle Map. When you access (or the system) a control you access it through its HANDLE. If you try and access a control from a thread that which did not create it, the thread will not have a HANDLE for it and the call will fail. In MFC and using the API this can be delt with sometimes by placing a duplicate HANDLE in the calling threads Temporary or Permanent Handle Map. But that pre-supposes that the control will exist through out your call to the control. Generally this is ok because when you duplicate a handle its resourse count is automatically incremented by one. But, if I'm not mistaken you must manually decrement it by calling CloseHandle.
Brandon Parker
|
|
|
|

|
You can try to access the elements, but your app will freeze !
|
|
|
|

|
Good work James!
Nish
p.s. As soon as this message is posted I am gonna give you a 5.
If I am awake and my eyes are closed, it does not necessarily mean that I am thinking of naked women.
|
|
|
|

|
Thanks Nish
James
Sonork: Hasaki
"I left there in the morning
with their God tucked underneath my arm
their half-assed smiles and the book of rules.
So I asked this God a question
and by way of firm reply,
He said - I'm not the kind you have to wind up on Sundays."
"Wind Up" from Aqualung, Jethro Tull 1971
|
|
|
|
 |
|
|
General News Suggestion Question Bug Answer Joke Rant Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.
|
Teaches how to use the Invoke functionality so that interaction with UI elements can be safely done.
| Type | Article |
| Licence | BSD |
| First Posted | 5 Apr 2002 |
| Views | 200,275 |
| Downloads | 4,669 |
| Bookmarked | 84 times |
|
|