views:

237

answers:

3

I am looking into someone else's code and do not have much experience with anything to do with multi-threading. I came across this line of code:

BeginInvoke((MethodInvoker)delegate() { btnCalibrate.PerformClick(); });

I was wondering why do this when just this would have worked: btnCalibrate.PerformClick();

Thanks for your answers.

+3  A: 

Because if this code ran in a different thread from the one that created the button in the GUI, an exception would be thrown. Background threads cannot directly call methods on the GUI because the GUI is not thread safe.

Of course, this is just the rational reason for using BeginInvoke! But it is not unusual to find code that contains incantations or magic spells that have just been put in for no good reason, because the author had seen another example that did it that way and so assumed it must be necessary in all cases. If the code you're working with is single threaded, then it is unnecessary.

In particular, Windows Forms itself is resolutely single-threaded. All operations on all windows and controls happen on the same thread, and all their events fire on that same thread. The use of the one GUI thread is partitioned out through a message loop that continuously runs on the thread and reads messages from a queue. The purpose of BeginInvoke is ultimately to post a message to that queue, effectively saying "When you have a moment, please run this chunk of code."

Daniel Earwicker
Forgive me for my ignorance but btnCalibrate exists on "Form1" and the above code also exists on "Form1". The above code exists on a function called `void wm_OnWiimoteChanged` on Form1.cs and `btnCalibrate_performClick` exists on the same file. So where does a "different thread" come from?
aip.cd.aish
If you could also suggest some article that would educate me on this topic, that would be much appreciated! :)
aip.cd.aish
@aip.cd.aish: I liked this article: http://msdn.microsoft.com/en-us/magazine/cc188732.aspxAh... memories...
codekaizen
Oops... wrong article... it was actually Ian Griffiths: http://msdn.microsoft.com/en-us/magazine/cc300429.aspxBoth look decent, though.
codekaizen
@codekaizen: Thanks for the links. I will read through them. I am still confused about one thing - if BeginInvoke is used to access information in one thread from a different thread. There should exist two threads to begin with. But in this app - everything above is in just 1 form. Do forms somehow have 2 threads?
aip.cd.aish
@aip.cd.aish - sorry, I was in the shower. See update.
Daniel Earwicker
@Earwicker: Thank you for your answer. As far as I see I think this app is single threaded, but if I discover otherwise, this knowledge will definitely help.
aip.cd.aish
+1  A: 

To expand on @Earwicker a bit - Control.BeginInvoke is a way to transfer a call from one thread to the thread which owns the Control instance. In the background, it is using Win32 messages via a Win32 function called PostMessage to marshal the call to the method PerformClick on the owning thread. This is needed due to Win32 Windows, and by extension WinForms Controls, being safe to access only from the thread that created it, which is usually the single thread running the GUI.

The reason BeginInvoke is used vs. Invoke is because the former uses PostMessage and returns right away, while the latter uses SendMessage under the covers, and needs to wait PostMessage and waits for the reply from the GUI thread. This can delay the thread making the call or even lockup the application, so BeginInvoke is the better choice here.

codekaizen
Invoke does NOT use SendMessage. It uses PostMessage also, but waits for the result before returning.
Andy
@Andy - Fine. While in practice, you use BeginInvoke/Invoke in a manner similar to PostMessage/SendMessage, the behavior of SendMessage (jumping the message queue and heading right to the WinProc) vs. PostMessage/Wait is different enough to make this distinction work preserving. I'd just never noticed since there was no practical difference for my usages.
codekaizen
+1  A: 

@aip.cd.aish

So where does a "different thread" come from? <-- BeginInvoke will make the call run on a different thread in the Threadpool

For threading I recommend http://www.albahari.com/threading/

Benedict
@benedict: Thanks for the link. Looks very in-depth. I will definitely read through it. But about the "different thread", so BeginInvoke will run the call on a different thread? but my question was why do this? why not run that call on the same thread? all the code is in the same form...so I think same thread anyway. I know I am missing something here, it would be great if someone can point it out to me.
aip.cd.aish
@aip.cd.aish: What you are missing here is that the same segments of code can run in several copies at the same time, in parallel. If your form has one method, it can still be run simultaneously on 2 threads, which introduces all the subtleties described in the linked articles.
Alan