Geeks With Blogs
The Coding Bloke An ordinary bloke trying to keep up with the world of programming

Its been quite some time since I last post on the subject of the AsyncOperationService (in fact quite some since I posted period).

In real world use I found some common patterns emerge that has lead me to make a few small adjustments to eliminate some common duplication I was seeing in code consuming the service.  Here is the new code file for my ever so slightly improved AsyncOperationService in its entirety.

 

AsyncOperationService
using System;
using System.Windows;
using System.Collections.Generic;

namespace CodingBloke.AsyncOperations
{
    public delegate void AsyncOperation(Action<Exception> completed);

    public static partial class AsyncOperationService
    {
        public static void Run(this IEnumerable<AsyncOperation> asyncOps, Action<Exception> completed)
        {
            IEnumerator<AsyncOperation> enumerator = asyncOps.GetEnumerator();

            Action<Exception> disposeAndComplete = (exception) =>
            {
                enumerator.Dispose();
                Deployment.Current.Dispatcher.BeginInvoke(() => completed(exception));
            };

            Action executeNextOp = null;
            executeNextOp = () =>
            {
                bool asyncCallbackExecuted = false;
                bool sequenceIncomplete = true;

                try
                {
                    sequenceIncomplete = enumerator.MoveNext();
                    if (sequenceIncomplete)
                    {
                        enumerator.Current((asyncError) =>
                        {
                            if (!asyncCallbackExecuted)
                            {
                                asyncCallbackExecuted = true;

                                if (asyncError == null)
                                    executeNextOp();
                                else
                                    disposeAndComplete(asyncError);
                            }
                        });
                    }
                }
                catch (Exception syncError)
                {
                    disposeAndComplete(syncError);
                }

                if (!sequenceIncomplete)
                    disposeAndComplete(null);

            };
            executeNextOp();
        }

        public static AsyncOperation SwitchToUIThread()
        {
            return completed => Deployment.Current.Dispatcher.BeginInvoke(() => completed(null));
        }

        public static AsyncOperation SwitchToBackgroundThread()
        {
            return completed => System.Threading.ThreadPool.QueueUserWorkItem(o => completed(null));
        }

    }

}

 

Callback from Run method now executes on the UI Thread

One pattern I noticed was that in the delegate provided for the Run callback there was always some code to switch to the UI Thread. It seemed to me that it would be useful if the Run callback was always guaranteed to run on the UI thread thereby eliminating this duplication.

SwitchToUIThread Method

Sections of code executing within the main sequence (the function yielding AsyncOperations) will run in whatever thread the yielded operation called its completed delegate on.  As a result when code in the main sequence needed to modify UI elements it would often include an ugly one-liner to yield a AsyncOperation delegate that would switch code execution on to the UI Thread.  I thought encapsulating the one liner into static method on the service class would be useful, hence SwitchToUIThread was added.

SwitchToBackgroundThread Method

At least for sake of symmetry I thought it would also be good add a SwitchToBackgroundThread which would take the code execution off the current thread and into a background thread.  This would be handy when using some async components that always  complete on the UI Thread. In some cases there may still be some considerable processing to do before UI components need meddling with.  The SwitchToBackgroundThread allows the main sequence to jump out of the UI Thread.

 

Daft Demo

By way of a really silly demonstration do the following:

  • Create yourself an fresh new Silverlight Application project in Visual Studio
  • Add a code file called “AsyncOperationService” to the project and paste the above code into it
  • To the MainPage.xaml add a single ListBox and give it the x:Name “lstResults”
  • Now past the code below into MainPage.xaml.cs
MainPage.xaml.cs
using System.Collections.Generic;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using CodingBloke.AsyncOperations;

namespace SilverlightApplication1
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            Loaded += MainPage_Loaded;
        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            ExecuteDaftOperations();
        }

        string CurrentThreadID { get { return Thread.CurrentThread.ManagedThreadId.ToString(); } }

        IEnumerable<AsyncOperation> DaftOperations(List<string> result)
        {
            result.Add("Starting on thread: " + CurrentThreadID);

            yield return AsyncOperationService.SwitchToBackgroundThread();
            result.Add("Switched to background thread: " + CurrentThreadID);

            yield return AsyncOperationService.SwitchToUIThread();
            result.Add("Switched back to UI thread: " + CurrentThreadID);

            yield return AsyncOperationService.SwitchToBackgroundThread();
            result.Add("Switched back to background thread: " + CurrentThreadID);

            yield return AsyncOperationService.SwitchToBackgroundThread();
            result.Add("Switching from one background thread to another: " + CurrentThreadID);
        }

        void ExecuteDaftOperations()
        {
            var result = new List<string>();

            DaftOperations(result).Run(err =>
            {
                if (err == null)
                {
                    result.Add("Completed on thread: " + CurrentThreadID);
                    lstResults.ItemsSource = result;
                }
                else
                {
                    MessageBox.Show("Oops something bad happened");
                }
            });

        }
    }
}

 

When you run this app a few times you’ll notice that execution always starts and ends on Thread 1 (the UI thread) whereas the background thread chosen may vary.

You might wonder why we would ever want to switch to a background thread when we know we are already on a background thread.  Well in some cases code may be executing as a result of some callback or event that represents the completion of an expensive task.  This expensive task may well have significant resources allocated and is waiting for its callback or event to return before releasing them.  Yielding a SwitchToBackgroundThread allows the outstanding callback or event to return and the existing callstack to wind all the way back, thus releasing potentially significant resources.  In fact for that matter the same may also be true of SwitchToUIThread.

Posted on Saturday, July 30, 2011 8:07 PM Silverlight , AsyncOperationService | Back to top


Comments on this post: Simple Asynchronous Operation Runner – Revisited

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © codingbloke | Powered by: GeeksWithBlogs.net