Introduction
You are developing this shiny new multithreaded .NET application that has this spanking new UI. You are done coding and it's time for giving a demo to your boss. And while running the demo, the UI hangs. Just hangs. Damn it, you say, it was working fine on my PC. You go back to your source and try to figure out why that happened. After a lot of poking around/debugging/swearing, you notice that you call this simple property on a control and it never returns. Or sometimes, it returns, but doesn't update the UI correctly. You then read something about calling BeginInvoke
. What's that, you wonder. This article will try to answer that. Note that this article will be dealing with the BeginInvoke
method on the System.Windows.Forms.Control
and not the BeginInvoke
method that can be called on delegates to run them asynchronously.
What does BeginInvoke do?
According to MSDN, Control.BeginInvoke
"Executes the specified delegate asynchronously on the thread that the control's underlying handle was created on". It basically takes a delegate and runs it on the thread that created the control on which you called BeginInvoke
. Why do I need to do that, you ask. To understand why, you have to know about how Windows processes UI actions.
Message queue and Message pumping
Every Windows application you create is provided with a thread on startup. It's this thread that calls the Main
method of your application. For a console application, you can write Main
to accept user input and process them. For a GUI application, things are radically different. GUI applications are event based, which means that there needs to be some entity to process and fire events. Windows manages this by creating a message queue for your application. All UI related actions get translated to messages that get posted to this message queue 1. Now, you need someone to read the messages from the queue and call appropriate event handlers. That's what a message pump is for. It basically is a loop that waits for someone to post a message to the queue. Once someone does, it dequeues it and calls the associated event handler. It can be thought of as:
Message message = null;
while ((message = GetNextMessage()) != null)
{
ProcessMessage(message);
}
}
Where does the message pump run? On the Main
thread, of course. So a typical GUI application, after doing some initialization in the Main
method, will then start running the message pump. The while
loop will exit only when the application closes, so that means the Main
thread can't do anything else once the message pump starts. In .NET GUI applications, the Application.Run
method takes care of message pumping. You'll see that every .NET GUI application's Main
method looks like this:
public static void Main(string[] args)
{
Form f = new Form();
Application.Run(f);
}
That fits in nicely with our theory, Application.Run
will run the message loop on the thread on which it is called. In our case, it is the main thread and you can verify that the thread doesn't get past Application.Run
until the application closes by running it under the debugger. As an aside, modal dialogs (shown using Form.ShowDialog
) have their own message pump whereas modeless dialogs (shown using Form.Show
) don't. Which means that you can call Form.ShowDialog
from any thread but Form.Show
requires the calling thread to be running a message pump. Note that if you call Form.Show
from a UI event handler, both the new form and the current form will be sharing the same message pump, which means that if one form is stuck executing one of the event handlers, the other one won't be usable too.
The One Rule
One of the cardinal rules of Windows GUI programming is that only the thread that created a control can access and/or modify its contents (except for a few documented exceptions). Try doing it from any other thread and you'll get unpredictable behavior ranging from deadlock, to exceptions to a half updated UI. The right way then to update a control from another thread is to post an appropriate message to the application message queue. When the message pump gets around to executing that message, the control will get updated, on the same thread that created it (remember, the message pump runs on the main thread).
Let's go deeper into the Win32 world. There are two fundamental Win32 API calls to access and/or modify a control, SendMessage
and PostMessage
. There is a big difference in the way the two execute. The major difference is that SendMessage
blocks the caller till the message gets processed by the message pump whereas PostMessage
returns immediately. The subtle but important difference is that messages sent using SendMessage
aren't queued in the message queue whereas PostMessage
messages are. SendMessage
messages are directly "sent" to the message pump. The message pump retrieves and processes messages sent using SendMessage
before looking into those in the message queue. Effectively, there are then two queues, one for SendMessage
messages and one for PostMessage
messages (which is what we call the message queue). The message pump processes all messages in the first queue before starting with the second. An interesting observation is that if code does a SendMessage
from within the message pumping thread, the window procedure gets called directly, that is, it doesn't go through the message pump.
Why The One Rule Exists
Now it should be fairly obvious as to why the rule is around. The first reason is the blocking nature of SendMessage
. Imagine a situation where the message pumping thread is waiting for your thread to complete and your thread does a SendMessage
. SendMessage
will return only after the message pump has processed the message, but the message pump is stuck waiting for the thread to complete. Deadlock, and we know it's not exactly fun.
In .NET, property/method calls on a Control
object translate to SendMessage
calls, so you can easily see how it can turn nasty. PostMessage
posts a message to the queue and returns immediately, so there is no chance of deadlock.
The second reason is more subtle. Because there are effectively two queues, it's quite possible that at one point in time, both queues have the same message but with different parameters. Let's assume the message sets the text of a control and that you've used SendMessage
. The message pump first processes SendMessage
messages and the text is set to a particular value. It next starts processing messages in the message queue (sent using PostMessage
). Along comes the same message from the message queue and it overwrites the previous value with its parameters. You never know when the message from the message queue was posted, it's very much possible that it was posted before you issued SendMessage
. In that case, your most recent update gets overwritten with an older one. If the SendMessage
message was to read the text of the control, it'll return a wrong value because the PostMessage
message that executes later will set it to something else and that's what will be visible in the UI.
If instead of SendMessage
, you had used PostMessage
, then both messages would have gone to the same queue and barring race conditions as to who posted first to the queue, the order would have been preserved.
There is one other issue with using SendMessage
that is not obvious at all. SendMessage
, while blocking the current thread, continues to process messages that are sent to the message pump using SendMessage
or one of the functions that send nonqueued[^] messages. This means that your code must be prepared to handle incoming messages when blocked on a SendMessage
call. This can cause problems if your code depends on SendMessage
to be a truly blocking function and doesn't use any other synchronization mechanism. For e.g., consider this piece of code:
void ButtonClick_Handler(object sender, EventArgs e)
{
int val = count;
SendMessage(...);
Console.WriteLine(val == count);
}
void SomeOtherMessageHandler()
{
count++;
}
When the above piece of code is blocking on the SendMessage
and someone else does a SendMessage
to this application that causes SomeOtherMessageHandler
to execute, then val
will not be equal to count
. The core of the issue is that SomeOtherMessageHandler
will execute even if ButtonClick_Handler
is blocked on SendMessage
, which is not what most people expect. To avoid such problems, Windows provides the SendMessageTimeout
API function, which when called with the right parameters, will prevent pumping of nonqueued messages when blocked on sending a message. However, all .NET UI controls use SendMessage
, so you can't avoid the problem, unless you are planning on doing P/Invoke.
If you're still not convinced that SendMessage
from a different thread is not a great idea, see this post for a great practical example.
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
: This bool
property returns true
if the thread on which this property is called is not the thread that created this control. Basically, if InvokeRequired
returns true
, you need to call one of the two Invoke methods.
BeginInvoke
: This is a functionally similar to the PostMessage
API function. It posts a message to the queue and returns immediately without waiting for the message to be processed. BeginInvoke
returns an IAsyncResult
, just like the BeginInvoke
method on any delegate. And you can use IAsyncResult
to wait for the message to be processed, just as usual. And you can call EndInvoke
to get return values or out
parameter values, as usual.
Invoke
: This is like the SendMessage
API function in that it waits till the message gets processed, but it does not do a SendMessage
internally, it also does a PostMessage
. 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 with SendMessage
won't happen. Invoke
returns the value that the function wrapped by the delegate returned.
A typical piece of code using BeginInvoke
will look like this:
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)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new SetLabelTextDelegate(SetLabelText),
new object[] {number});
return;
}
label.Text = number.ToString();
}
}
Assigning a value to label.Text
directly in SetLabelText
will result in a SendMessage
to the underlying control from the current thread and we saw in excruciating detail why it is troublesome. So we use BeginInvoke
, passing the same function as the delegate parameter, which will post a message to the handle of this. When the message pump dispatches the message, it'll call SetLabelText
and in that invocation, InvokeRequired
will return false
. We then set the Label
's text, confident that we are on the right thread.
The BCL provides a MethodInvoker
delegate, which you can use if your wrapped function takes no parameters and returns void
. You can also reuse the EventHandler
delegate in case your wrapped function's signature matches it. The MSDN documentation says using these delegates instead of our own custom delegates will result in faster execution.
Both BeginInvoke
and Invoke
check if they are called on the correct thread (the thread that created the control) and if so, directly update the control instead of doing the PostMessage
thing. Apart from performance benefits, it also prevents deadlock if you call Invoke
from within a method already running on the UI thread.
Invoke
and BeginInvoke
Which function to use, you ask. It really depends on your requirement. If you want your UI update to complete before proceeding, you use Invoke
. If there is no such requirement, I'd suggest using BeginInvoke
, as it makes the thread calling it seemingly "faster". There are a few gotcha's with BeginInvoke
though.
- If the function you are calling via
BeginInvoke
accesses shared state (state shared between the UI thread and other threads), you are in trouble. The state might change between the time you called BeginInvoke
and when the wrapped function actually executes, leading to hard to find timing problems.
- If you are passing reference parameters to the function called via
BeginInvoke
, then you must make sure that no one else modifies the passed object before the function completes. Usually, people clone the object before passing it to BeginInvoke
, which avoids the problem altogether.
Note that the above points are valid for any function that you run as a thread. They're not so obvious when using BeginInvoke
, because BeginInvoke
doesn't actually create a thread and instead runs the wrapped function on an already existing thread (the UI thread). It still means that there are two threads, so you want to take the same care protecting your shared variables.
If your code is based on .NET 1.1 (including SP1), there is a bug in the framework implementation of Invoke
which could cause it to hang indefinitely on multiprocessor machines. The bug has been fixed in .NET 2.0 and as this KB article says, there is a hotfix available for 1.1, but it can only be obtained by calling PSS. You could always call BeginInvoke
and use a custom signalling mechanism instead. Here is the description of the actual problem, in case you're interested.
A warning
Control.BeginInvoke
, which is what we have been discussing so far, works slightly differently from Delegate.BeginInvoke
. Delegate.BeginInvoke
grabs a threadpool thread and executes the passed delegate on that thread. Control.BeginInvoke
does not use a threadpool thread, it does a PostMessage
to the target window handle and returns. This is crucial because if it uses threads, then there is no guarantee to the order in which messages are posted and processed by the application. Also, unlike Delegate.BeginInvoke
, Control.BeginInvoke
doesn't require every call to BeginInvoke
to be matched by an EndInvoke
. Of course, if you are using the return value of the method and/or out
or ref
values, then you need to call it anyway.
Conclusion
Judging from the number of posts in the C# forum resulting from the improper use of BeginInvoke
, this seems to be a poorly understood topic. Hopefully this article will help clarify things a bit. In .NET 2.0, the CLR straightaway throws an exception if you attempt to do a cross thread UI update. In fact, I'd suggest compiling your app for the 2.0 platform and running the app through all possible scenarios, just to make sure there are no inadvertent cross thread UI updates.
Appendix
Note 1 Not entirely true, as explained later in the article. Some of the messages are posted to the message queue (PostMessage
) and some are directly sent to the message pump (SendMessage
). Sent messages are processed first before processing messages in the message queue.
History
- Initial post on 07-May-2005.
- Added explanation for The One Rule. Corrected major error in description of
SendMessage
[11-May-2005].
- Beefed up the "Why the One Rule exists" section and made miscellaneous modifications [5-June-2005].