This Sunday's tip... Safe Windows Forms Multithreading

My girlfriend calls the place where I'm in “pencil sharpening”.  It describes a period of time where you're “preparing” to do some work by doing things that don't really need to be done rather than getting stuck in, mainly because I don't really do what I have to do today!  Anyway, it's at times like this I usually fire up Reflector and start prodding around in the BCL.

I have a background in Win32 and, as such, I've always known that if you're writing multithreaded UI, it's a very bad idea to manipulate the UI through a worker thread.  This can cause all kinds of thread safety issues and, oh god, it's just too horrible to think about - weird hard to find bugs that only happen one in a thousand times, and never with a debugger running.  Shudder! 

In .NET, if you need to manipulate the UI from a worker thread, you need to use the Invoke method on the form.  This handles marshalling the call from the worker thread to the main UI thread.  (Which underneath the covers if the thread that's calling down to the GetMessage...DispatchMessage loop that underpins all Windows UI.)  This gets around the thread safety problems - your worker thread is suspended while the UI is updated from the thread.  Great. 

This means you end up with code that looks like this:

private void FudgeStatusBar()
{
    if(INeedToCallInvoke)
    {
        this.Invoke(new FudgeStatusBarDelegate(FudgeStatusBar));
        return;
    }

    // by the time we get here, we're in the main UI thread.
}

So, in order to do my INeedToCallInvoke method, usually in the constructor I capture the thread ID by calling AppDomain.GetCurrentThreadId() and storing it in a private field.  INeedToCallInvoke would then check the current ID of the thread, compare it to the field and return true if I was in a different thread.

I've known that you're supposed to use Invoke for a couple of years, but what I have only found out just recently is that there's a property on Control called InvokeRequired.  This does the work for you, so what you actually need to do is this:

private void FudgeStatusBar()
{
    if(InvokeRequired)
    {
        this.Invoke(new FudgeStatusBarDelegate(FudgeStatusBar));
        return;
    }

    // by the time we get here, we're in the main UI thread.
}

If you decompile InvokeRequired (using your favourite decompiler) you'll discover this:

public bool get_InvokeRequired()
{
    HandleRef ref1;
    Control control1;
    int num1;
    int num2;
    int num3;
    if (this.IsHandleCreated)
    {
         ref1..ctor(this, this.Handle);
    }
    else
    { 
        control1 = this.FindMarshalingControl();
        if (!control1.IsHandleCreated)
        {
             return false; 
        }
        ref1..ctor(control1, control1.Handle); 
    }
    num2 = SafeNativeMethods.GetWindowThreadProcessId(ref1, &(num1));
    num3 = SafeNativeMethods.GetCurrentThreadId();
    return (num2 != num3);
}

This is obviously a bit more sophisticated than capturing the ID of the thread.  I guess that here, until the handle is created, we're just affecting the internal state of the object, hence no message loop, hence no issues with thread safety.  Then, at the end, the property is checking to see if the window's thread ID is the same as the current thread ID.  When I read this, I remembered about GetWindowThreadProcessId (MSDN)...

/P>Moral of the story - sometimes in .NET, you can understand 99% of how something works, but finding that other 1% can really make the difference!

1 Comment

Comments have been disabled for this content.