public class FormFoo : Form { Label label = new Label(); public static void Main() { Application.Run(new FormFoo()); } public FormFoo() { InitializeComponent(); Thread t = new Thread(new ThreadStart(ChangeLabel)); t.Start(); } private void ChangeLabel() { for (int i = 0; i<100; ++i) { SetLabelText(i); Thread.Sleep(1000); } } private delegate void SetLabelTextDelegate(int number); private void SetLabelText(int number) { // label.Text = number.ToString(); // Do NOT do this, as we are on a different thread. // Check if we need to call BeginInvoke. if (this.InvokeRequired) { // Pass the same function to BeginInvoke, // but the call would come on the correct // thread and InvokeRequired will be false. this.BeginInvoke(new SetLabelTextDelegate(SetLabelText), new object[] {number}); return; } label.Text = number.ToString(); } }
Why and when to call BeginInvoke
With all that stuff inside our head now, we can easily figure out the reason for the existence of BeginInvoke
. It essentially does a PostMessage
. Whenever you want to update a control from a thread that didn't create it, instead of directly calling the method/property to update it, you need to wrap it in a BeginInvoke
call. According to MSDN: "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". One of the invoke methods, you say, what are the others? Well, the Control
class provides one property and two methods to do the stuff we discussed.
InvokeRequired
: Thisbool
property returnstrue
if the thread on which this property is called is not the thread that created this control. Basically, ifInvokeRequired
returnstrue
, you need to call one of the two Invoke methods.
BeginInvoke
: This is a functionally similar to thePostMessage
API function. It posts a message to the queue and returns immediately without waiting for the message to be processed.BeginInvoke
returns anIAsyncResult
, just like theBeginInvoke
method on any delegate. And you can useIAsyncResult
to wait for the message to be processed, just as usual. And you can callEndInvoke
to get return values orout
parameter values, as usual.Invoke
: This is like theSendMessage
API function in that it waits till the message gets processed, but it does not do aSendMessage
internally, it also does aPostMessage
. The difference is that it waits till the delegate is executed on the UI thread before returning. So while there is a chance for the deadlock problem to occur, you can be sure that the other problems associated withSendMessage
won't happen.Invoke
returns the value that the function wrapped by the delegate returned.