views:

210

answers:

4

I've inherited code where BeginInvoke is called from the main thread (not a background thread, which is usually the pattern). I am trying to understand what it actually does in this scenario.

Does the method being called in the BeginInvoke get in line of messages that come down to the window? The docs say asynchronously, so that is my assumption.

How does the framework prioritize when to kick off the method called by BeginInvoke?

Edit: The code looks like this:

System.Action<bool> finalizeUI = delegate(bool open)
{
    try
    {
        // do somewhat time consuming stuff
    }
    finally
    {
        Cursor.Current = Cursors.Default;
    }
};

Cursor.Current = Cursors.WaitCursor;
BeginInvoke(finalizeUI, true);

This is happening in the Form_Load event.

+2  A: 

In the case BeginInvoke is called on a UI thread it will still go through the process of posting a Windows Message to the message queue where the message will wait to be processed. The delegate will run when the message is processed. This message is not prioritized in any way that's different than it being called from the background thread.

JaredPar
It doesn't wait.
Hans Passant
@nobugz, terrible wording on my part. I was trying to say the message itself will sit on the queue where it waits to be processed not that the caller waits. Cleaned the language.
JaredPar
Are you certain of this? BeginInvoke is a PostMessage rather than a SendMessage or SendNotifyMessage?
John Knoeller
@John, 100% sure. SendMessage is synchronous which doesn't work with an asynchronous BeginInvoke hence it uses PostMessage. You can view this yourself by looking at the implementation of Control.BeginInvoke in reflector. Actual call is in MarshaledInvoke.
JaredPar
JaredPar: Yes, SendMessage wouldn't work, but SendNotifyMessage would deliver it _before_ posted messages while still not waiting for the message to be delivered. Anyway, thanks for the clarification. I'll have a look.
John Knoeller
@John, interesting. Didn't know that about SendNotifyMessage. Haven't ever used the API but I would have assumed Send would equate to synchronous since it does most everywhere else
JaredPar
@JaredPar: It's not synchronous when the caller isn't on the thread that owns the window, The message goes into a special high-priority queue that's looked at before the normal message queue, and the dispatch happens inside GetMessage/PeekMessage rather than having the message returned. This is basically the normal cross-thread SendMessage queue, but SendNotifyMessage doesn't block and wait for the message to be dispatched.
John Knoeller
@JaredPar: the code that AngryHacker posted wouldn't be useful if Form.BeginInvoke didn't use PostMessage in that situation, so you must be right about that.
John Knoeller
+3  A: 

edit

Now that we see the code, it's clear that this is just a way to move some initialization out of Form_Load but still have it happen before the user can interact with the form.

The call to BeginInvoke is inside Form_load, and is not called on another object, so this is a call to Form.BeginInvoke. So what's happening is this.

  1. Form_Load passes a delegate to Form.BeginInvoke, this puts a message in the form's message queue that is ahead of all user input messages. It sets the cursor to a wait cursor.
  2. Form_Load returns, and the rest of form initialization is allowed to complete, the form most likely becomes visible at this point.
  3. Once the code falls into the message pump, the first thing is sees in the queue is the delegate, so it runs that.
  4. as the delegate completes, it changes the cursor back to the normal cursor, and returns
  5. profit!

original post below


I depends on the object that you call BeginInvoke on. If the object is derived from Control then Control.BeginInvoke will run on the thread that created the control. See JaredPar's answer.

But there is another pattern for the use of BeginInvoke. if the object is a delegate, then BeginInvoke runs the callback on a separate thread, one that may be created specifically for that purpose.

public class Foo
{
    ...
    public Object Bar(object arg)
    {
       // this function will run on a separate thread.
    }
}

...

// this delegate is used to Invoke Bar on Foo in separate thread, this must
// take the same arguments and return the same value as the Bar method of Foo
public delegate object FooBarCaller (object arg);

...

// call this on the main thread to invoke Foo.Bar on a background thread
//
public IAsyncResult BeginFooBar(AsyncCallback callback, object arg)
{
   Foo foo = new Foo();
   FooBarCaller caller = new FooBarCaller (foo.Bar);
   return caller.BeginInvoke (arg);
}

This pattern is one reason that BeginInvoke is called from the main thread rather than from a background thread.

John Knoeller
@John Knoeller, are you saying that in my scenario, BeginInvoke carries the weight of the thread context switch?
AngryHacker
@AngryHacker: No, now that you have shown your code, it's clear that this is just a PostMessage like JaredPar described, your BeginInvoke is a method on the form, and thus will _not_ run the delegate on a separate thread.
John Knoeller
+1  A: 

In this scenario I suspect the call looks like:

private void Button1_Click(object sender, ButtonClickEventArgs e)
{
    Control.BeginInvoke(new MethodInvoker(()=> /* code etc. */));
}

What's happening is that some code will run on on a threadpool thread, and update the control on the thread that created the control whereas if Control.Invoke was used, some code would run on the thread that created the control, and update the control on that thread as well.

SnOrfus
+1  A: 

Prior to widespread BackgroundWorker use, you had to synchronize back to the UI thread before doing any operations on Controls created on the UI thread (i.e. pretty much every Control).

There's a pretty good reference example here down in the "Thread-Safe Calls to a Windows Forms Control" section.

micahtan