The mammoth task of making a single-threaded UI, getting multi-threaded.

At the moment I’m working on a single-threaded WinForm UI that will have to consume WCF Services over the Internet. The problem of such a change is, that the UI is getting really languorous due to the service response times. That’s why I’m going to rewrite it for multi-threading.

At this blog-post I want to describe the problems I have to face, and the way I solved it.

1.) Invocation
All times you change or query a UI control within a thread, you’ll get an InvocationException due to the fact, that you are’nt allowed to do this within a thread. Instead of duplicating the code (like described in http://is.gd/c2zoY) I wrote a delegate which does this for me.

        public static void InvokeIfRequired(this Control window, Action method)
        {
            if (!window.InvokeRequired)
                method();
            else
                window.Invoke(new MethodInvoker(() => method()));
        }

That helped me to write code like:

        // Example
        this.InvokeIfRequired(() =>
        {
                cmdAddProject.Visible = context.AllowedToCreateInstantProjects;
                cmdEditProject.Visible = context.HasTheRightToFullAccess;
                cboProjects.Focus();
        });

2.) STA Threads and Too many Threads
The problem of Multi-Threading in UI is, that worker threads are always MTA Threads. But some UI interaction requires to use STA Threads. Another problem is, that if you call nested methods, you don’t want to open a new thread, if you’re already in a worker thread.

2a) STA Threads:
I don’t know a solution other than to create your own thread.

2b) Too many threads:
I assume: When we are in a separated thread, we don’t need to create a new thread. That’s the point. That’s why I’m using a [ThreadStatic] field to flag a thread as a worker thread.

        [ThreadStatic] private static bool isInSeparatedThread;

        ///  Executes a Call within a queued worker process 
        public static void ThreadedCall(this IWin32Window window, Action method)
        {
            var ctrl = window as Control;
            bool createdState = ctrl == null || ctrl.Created;

            // Do not force a new thread when a the caller already in a new thread
            // or the control has not even been created.
            if (!isInSeparatedThread && createdState)
            {
                var thread = new Thread(() =>
                {
                    isInSeparatedThread = true;
                    method();
                });
                thread.SetApartmentState(ApartmentState.STA);
                thread.Start();
            }
            else
            {
                // A Thread already started
                method();
            }
        }

That helped me to write code like:

            this.ThreadedCall(() =>
                {
                    ...
                    ...  // Although, if ThreadedCall is executed again, no new thread will be created.
                    ...
                });

3.) Exception Handling
And last but not least, error handling is a problem, because every thread needs to be encapsulated by a TRY/CATCH Block in order to handle exceptions properly. I managed this by writing the following extension method.

        [ThreadStatic] private static bool isInSafeCall;

        /// 
        /// Executes the Action Method in a safe way, in that exceptions are handled.
        /// 
        public static void SafeCall(this IWin32Window window, Action method)
        {
            // If the call has already been assured, so only executed the method inside 
            if (isInSafeCall)
            {
                method();
                return;
            }

            try
            {
                isInSafeCall = true;
                method();
            }
            catch (Exception exception)
            {
                ApplicationHelper.ShowException(window, null, exception);
            }
            finally
            {
                isInSafeCall = false;
            }
        }

That helped me to write code like:

            this.SafeCall(delegate
            {
                using (var listDialog = new ProjectListDialog(Connection))
                    listDialog.ShowDialog(this);

                RefreshContent();
            });

4.) Single Method Calls
I forgot something. Sometimes it’s necessary to ensure, that a code block is only called one time by a thread. Yeah, I know. You can use lock(), but I don’t want that other threads are waiting to execute the code. Think about failing connections. You don’t want to inform the user for every thread, that the connection has been broken. That’s why I implemented a Singleton extension method, that ensures, that a code part is only called one time.

        /// 
        /// Declares that a method is only called once.
        /// Second call's from other threads to the method will return immidiatly.
        /// 
        public static void Singleton(this IWin32Window window, AutoResetEvent locking, Action method)
        {
            // Return immidiatly, if the event could not aquired.
            if (!locking.WaitOne(0))
                return;

            try
            {
                method();
            }
            finally
            {
                locking.Set();
            }
        }

That helped me to write code like:

        private readonly static AutoResetEvent ShowExceptionLock = new AutoResetEvent(true);

        public static void ShowException(this IWin32Window parentWindow, Exception exception)
        {
            parentWindow.Singleton(ShowExceptionLock, () =>
                {
                    MessageBoxIcon icon = MessageBoxIcon.Exclamation;
                    parentWindow.Show(parentWindow, message, ProductLabel, MessageBoxButtons.OK, icon);
                });
        }

5). Combination:
And I combined some methods to create new functionality that I use many times.
For example that one:

        ///  Executes a Safe Call within a new Thread 
        public static void ThreadedSafeCall(this IWin32Window window, Action method)
        {
            window.ThreadedCall( 
                ()=> window.SafeCall(method)
            );
        }

6). Download:
If you like it, you can download the complete helper class and tell me your thoughts about it.

Cheers
- Gerhard

Download: Threading.cs

kick it on DotNetKicks.com

About these ads

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 106 other followers

%d bloggers like this: