Skip to content

Unexpected Results using InvokeRequired and Multi-threading Secrets Revealed!

Before you rush and view my blockquote and jump to the conclusion “oh, it’s just that standard threading issue”, well let me assure you this is different to the norm, and please.. read on for the sake of how long this stupid article turned out :)  Yesterday, was a very annoying day dealing with multi-threading in my app, you would think I would be good at this by now considering I blogged about it not too long ago acting like I knew what I was talking about. :)

Well, Alas, not all has changed; I still strongly standby my multi-threading sample and believe it still displays a correct way in which to launch a thread and update the UI correctly.

However, this morning I unveiled some very interesting points on threading.  The situation I found myself in yesterday involved a business application I have been working on that has been working peachy-keen all well and good until yesterday when one in every 5-10 re-builds would return our beloved:

Cross-thread operation not valid: Control ‘panelMain’ accessed from a thread other than the thread it was created on.

After laughing at myself and thinking “oh boy, just my luck… just finished blogged about multi-threading… So where is this problem originating from?” Well after looking high and low in my code, in search for a dreaded ommited (InvokeRequired) conditional test, alas I gave up.

I had handled all of my UI updating methods correctly by marshalling and invoking to the UI thread against the pattern I preach, BeginInvoke was being called correctly, no methods had been overlooked, and one of the fabbled gotcha’s some people omit, the return statement. But no, that was included too. So WHAT was going ON?!

Build after build, with my face on the desk, eyes glaring up at the screen, annoyed at spending half an hour on this already pressing F5 and waiting for this cross-thread issue to re-appear was like watching grass grow.  When the exception was encountered, it was throwing in Application.Run() and would not tell me the caller or I could not trail back the call stack to see the originator. But then BAM, I encountered something I could work with after cleaning the solution, shutting down VS, waving my hands, crossing my fingers and dancing (ok no dancing):

But what was this the VS gods gave me to work with? Are my eyes deceiving me? Does that say if InvokeRequired is true, then BeginInvoke and then return?, in which case the function would re-run and InvokeRequired would equal false? And this results in us now being outside the if statement, and InvokeRequired is still true when it should have been invoked and equal false and the UI update has failed??? Why didn’t BeginInvoke marshall off and tell the GUI to do the update? (Was that as hard for you to read as it was for me to write?)

OK so this is getting weirder, now VS is laughing at me, pulling out all the stops, picking on my weak side knowing I can’t debug internal methods with ease. So what do you do? You turn to Google and Stack Overflow! I couldn’t find anything on Google under these search terms (you can see I didn’t try very hard) with regard to what I am looking for. The search results were just too flooded with novices who were receiving cross-threading operation errors for VALID REASONS and had not seen my creating a fast and responsive UI with multi-threading examples!

So we turn to Stackoverflow; a loving, giving community filled with wonderful people who just want to help (and gain rep points). Such saints they are, but one person in particular, cpeterso which after consulting back and forth gave me the answers I seek. OK, two people in particular: an honourable mention must go out to bleevo who suggested I just ditch the return and use an else. hehe, it looked good in theory, but I simply think that would be blanketing an existent problem and not be truly repairing it. As to do that, would indicate a lack of faith in InvokeRequired as we definitely trust the return statement.

But a lack of faith in InvokeRequired was exactly the problem.

My fellow Stackoverflow friend alerted me to a few lesser known points about multi-threading which were uncovered in a link he sent me:


Now I was particularly interested in the one that is sitting there innocently: InvokeRequired may return unexpected results. WHAT THA!

I won’t paraphrase the entire article, but I suggest you take a look at primarily the top two points.

I will however display the second point, the point that mattered to me:


SOLUTION

InvokeRequired May Lie to You

Here is a typical code snippet for marshalling notifications to the GUI thread:

 delegate void MyHandlerDelegate();

void MyHandler()
{
    // "The BeginInvoke dance"
    if (this.InvokeRequired) // assuming this descends from Control
    {
        BeginInvoke( new MyHandlerDelegate(MyHandler) );
        return;
    }

    // assume we are on the main GUI thread
    ... do GUI stuff ...
}
[/sourecode]
This code works fine most of the time, but not all the time. How does Control.InvokeRequired know whether invoke is required? We can find the answer by looking at reflector. Here is how (simplified) code for InvokeRequired looks like:
public bool InvokeRequired
{
    get
    {
        HandleRef hWnd;
        int lpdwProcessId;
        if (this.IsHandleCreated)
        {
            hWnd = new HandleRef(this, this.Handle);
        }
        else
        {
            Control wrapper = this.FindMarshallingControl();
            if (!wrapper.IsHandleCreated)
            {
                return false; // <==========
            }
            hWnd = new HandleRef(wrapper, wrapper.Handle);
        }
        int windowThreadProcessId = SafeNativeMethods.GetWindowThreadProcessId(hWnd, out lpdwProcessId);
        int currentThreadId = SafeNativeMethods.GetCurrentThreadId();
        return (windowThreadProcessId != currentThreadId);
    }
}

When neither the control nor any of its parents have a valid window handle, InvokeRequired returns false unconditionally.

the BeginInvoke dance executes on a non-GUI thread. The "if" condition will return false, and the "GUI stuff" will end up being executed on a non-GUI thread leading to potentially disastrous concequences.

In real life, a situation when InvokeRequired cannot find a window handle may occur in at least two scenarios:

  • Early in the program startup, e.g. before calling Application.Run()
  • Early in the life of the control, e.g. while executing the control's constructor

 

LIGHTBULB MOMENT

For me, the sinking point was that remark, the root of my problems boiled down to exactly that; unexpected results were encountered and my cross-threading issue occurred due to the thread launching from my form's constructor, which in turn fired UI updates to occur.  I guess we should not overlook the ability to fire threads entirely from the constructor, just be very careful if you perform UI updates. Dah! I initially had my reasons for firing my BeginInvoke from the form's constructor, but it's now all moved to Page_Load. This appears to have alleviated all UI updating problems and exceptions.

I wasn't going to make this a huge article, I don't know what happened.


MORAL OF THE STORY

If you have multi-threading issues beyond the norm, try checking out that article first.

If that fails, check out my sample ;)

Have a great day. Twitch.

VN:F [1.9.10_1130]
Rating: 5.0/5 (1 vote cast)
VN:F [1.9.10_1130]
Rating: 0 (from 0 votes)
Unexpected Results using InvokeRequired and Multi-threading Secrets Revealed!, 5.0 out of 5 based on 1 rating
Bookmark and Share
kick it on DotNetKicks.com
Shout it

NOW, FOR A WORD FROM OUR SPONSORS

5 Comments

  1. Try This:

    internal static class EventInvoker
    {
    public static void Invoke(Delegate method,
    object[] args)
    {
    if (method != null)
    {
    Delegate[] delegates = method.GetInvocationList();

    if (delegates.Length > 0)
    {
    for(int i = 0; i < delegates.Length; i++)
    {
    Delegate del = delegates[i];

    if (del.Target is System.ComponentModel.ISynchronizeInvoke)
    {
    ((System.ComponentModel.ISynchronizeInvoke) del.Target).Invoke (del, args);
    }
    else
    {
    del.DynamicInvoke(args);
    }
    }
    }
    }
    }
    }

    Then in Client:

    public event EventHandler Paused;
    private void OnPaused()
    {
    if (Paused != null)
    {
    EventInvoker.Invoke(Paused, new object[] {this, EventArgs.Empty});
    }
    }

    VA:F [1.9.10_1130]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.10_1130]
    Rating: 0 (from 0 votes)
    Posted on 10-Feb-09 at 11:04 am | Permalink
  2. Graham O'Neale

    Are you describing a different way in which to invoke there Kazi?
    I’m not exactly sure what it is set out to achieve. Thanks for your comment though.

    VA:F [1.9.10_1130]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.10_1130]
    Rating: 0 (from 0 votes)
    Posted on 10-Feb-09 at 12:15 pm | Permalink
  3. This is a simple way to raise event or call method from a child thread to update control which does not belongs to that thread.

    VA:F [1.9.10_1130]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.10_1130]
    Rating: 0 (from 0 votes)
    Posted on 10-Feb-09 at 12:22 pm | Permalink
  4. Esben Bjerregaard

    Thanx alot! -you just saved me alot of time.

    Just goggled this specific problem and ended with trillions of hits on basic Cross-Thread stuff

    VA:F [1.9.10_1130]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.10_1130]
    Rating: 0 (from 0 votes)
    Posted on 09-Jun-09 at 6:27 am | Permalink
  5. This very detailed and very helpful. We were having a problem with GUI. Even though I am a C++ programmer, I was quickly able to understand and fix the C# problem after reading this article.

    Thanks a lot for submitting this valuable stuff. Keep it up.

    VA:F [1.9.10_1130]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.10_1130]
    Rating: 0 (from 0 votes)
    Posted on 09-Dec-09 at 11:52 pm | Permalink

One Trackback/Pingback

  1. DotNetShoutout on 10-Feb-09 at 10:32 am

    Unexpected Results using InvokeRequired and Multi-threading Secrets Revealed!…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

    VA:F [1.9.10_1130]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.10_1130]
    Rating: 0 (from 0 votes)

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*

My name is Graham O'Neale and I'm a software architect from Gold Coast, Australia. I am an overtime thinker, full time coder and awake part time in the real world. I have a keen interest in software development, particularly in the realm of programming (C#, ASP.NET, ASP.NET MVC, LINQ (2 SQL), Entity Framework, Silverlight, Blend, WCF, WPF) and a keen interest in the cutting edge and innovation. I have a new found love for design patterns, ALT.NET practices and well crafted software architecture. The purpose of this blog is to express any thoughts, findings, tips and gripes along my travels in the wonderful world of coding and technology...