Friday, 8 August 2014

Parallel Programming 01

In my previous post we had an intro to the TPL and the async/await keywords and that they're the same thing uisng console apps, in this post it's going to get more interesting we're going to work with these in a winRT application, what I made was a UI with two progress bars, two Textbox's and a button, kinda looks like so


Not going to win any design awards but gives you an idea of what i'll be referring to. To get started we created a simple event handler for the click event of the button.

void Start_Button_Click(object sender, RoutedEventArgs e)
{
    //start the first long running task
    LongRunningTask(Progress00_ProgresBar);

    //print something to the textbox
    TextBoxOne.Text = "First Progress Bar Complete";
           
    //show a message to the user
    new MessageDialog("Good Times").ShowAsync();

    //start the second long running task
    LongRunningTask(Progress01_ProgresBar);

    //print something to the second textbox
    TextBoxTwo.Text = "Second Progress Bar Complete";

}

and for our LongRunnting Task we simply made

public void LongRunningTask(ProgressBar pb)
{
    for (var i = 0; i <= 100; i+=20)
    {
        Task.Delay(1000).Wait();
        pb.Value = i;
    }
}

now what you'd call rocket science but lets push on, now what would you expect to happen here? I'll tell you what I'd expect
  • i would expect:
  • the first progress bar to increment 5 times in 5s 
  • the first textbox to fill in once it's complete
  • a dialog to show up with the words good times
  • immediately with the dialog still visible for the second progress bar to start same as the first
  • then finally for the second text box to be populated
  • all the while i would expect the UI to stay frozen.
unfortunately the only thing I got right was that the UI would freeze for the 10s, all the code fired in the background and once the event was finished the UI was updated.

Let's try and fix this, add the async keyword to our long running function, make it return a Task and change the Task.Delay(1000).Wait() to await Task.Delay(1000);

public async Task LongRunningTask(ProgressBar pb)
{
    for (var i = 0; i <= 100; i += 20)
    {
        await Task.Delay(1000);
        pb.Value = i;               
    }

}

now with that minor change we get much different behavior as soon as we hit start:

  • both textboxes are populated immediately 
  • both progress bars start immediately 
  • the dialog appeared immediately
  • the UI was responsive 
not what we we're going for but a whole lot better than what we had, basically we fired everything asynchronously and didn't await for anything to complete, so it all fired at the same time.

now let's modify our click event handler, make it async and await the progress bars.

async void Start_Button_Click(object sender, RoutedEventArgs e)
{
    //start the first long running task in parallel
    await LongRunningTask(Progress00_ProgresBar);

    //print something to the textbox
    TextBoxOne.Text = "First Progress Bar Complete";

    //show a message to the user
    new MessageDialog("Good Times").ShowAsync();

    //start the second long running task in parallel
    await LongRunningTask(Progress01_ProgresBar);

    //print something to the second textbox
    TextBoxTwo.Text = "Second Progress Bar Complete";
}

this time

  • the first porgress bar starts, when it completes
  • the first textbox is populated
  • the dialog appears
  • immediately after the first textbox is populated the second progress bar starts
  • once it's done the second textbox is populated.
  • the whole time the UI is responsive

much closer to what we where looking for, let's make one more modification add the await keyword to the message dialog.

await new MessageDialog("Good Times").ShowAsync();

Now instead of the second progress bar starting immediately it waits for the user to dismiss the Dialog.


So that was easy... now let's try it without the async/await keywords and just use tasks.

void Start_Button_Click(object sender, RoutedEventArgs e)
{
    //start the first long running task in parallel
    Task.Run(() => LongRunningTask(Progress00_ProgresBar))
        .ContinueWith(tr =>
        {
            //print something to the textbox
            TextBoxOne.Text = "First Progress Bar Complete";
        }).ContinueWith(async tr =>
        {
            //show a message to the user
            await new MessageDialog("Good Times").ShowAsync();
        }).ContinueWith(tr =>
        {
            //start the second long running task in parallel
            Task.Run(() => LongRunningTask(Progress01_ProgresBar));
        }).ContinueWith(tr => {
            //print something to the second textbox
            TextBoxTwo.Text = "Second Progress Bar Complete";   
        });

}

so instead of using the async/await we used the task.run().ContinueWith, now first thing yes this is valid you can keep chaining ContinueWiths, i'm sure there's a limit, but i don't know what it is. Howerver we're going to have a different problem, if we run this we'll get an error.

"The application called an interface that was marshalled for a different thread."

this brings us to another caveat, in winRT applications there's a UI thread,  It's the only allowed to update the user interface, so when we create tasks, we can't update the UI from within them directly. there is a construct that will allow us to reach out and update the thread from a task

await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => /* function to run on the UI */);

so lets modify our long running task to use it.

async Task<int> LongRunningTask(ProgressBar pb)
{
    for (var i = 0; i <= 100; i += 20)
    {
        await Task.Delay(1000);
        await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => pb.Value = i);
    }
    return 0;

}

we also have to update our event handler because of the textboxes and dialog.

void Start_Button_Click(object sender, RoutedEventArgs e)
{
    //start the first long running task in parallel
    Task.Run(() => LongRunningTask(Progress00_ProgresBar))
        .ContinueWith(async tr =>
            await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
            () => TextBoxOne.Text = "First Progress Bar Complete"))
        .ContinueWith(tr =>
            this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
            async () => await new MessageDialog("Good Times").ShowAsync()))
        .ContinueWith(tr =>
            LongRunningTask(Progress01_ProgresBar).Wait())
        .ContinueWith(async tr =>
            await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
            () => TextBoxTwo.Text = "Second Progress Bar Complete"));

}

now event here, we don't wait for the dialog, needless to say the complexity is far greater than with the async await keywords.

Friday, 1 August 2014

Parallel Programming 00

Parallel programming is exactly what it sounds like, doing things at the same time, luckily because of c# constructs namely the Task Parallel Library and the async/await magic it's a lot easier than it sounds as well.

A Tiny history lesson, before c# 4.0 we had to use Threads and the ThreadPool for asynchronous programming; this was hard. Now we have the Task Parallel library which made life much easier, after that the magical async/await keywords appeared. I say magical, because that's exactly what they are they're just syntactical fairy dust that actually turns into the task.run().ContinueWith.

Lets start with a console application, take a look at the following:

using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace Example00
{
    class Program
    {
        static void Main(string[] args)
        {
            var sw = new Stopwatch();
            sw.Start();
            var num0 = GetNumber(3);
            var num1 = GetNumber(4);
            sw.Stop();

            Console.WriteLine("{0} + {1} = {2} in {3}'s"num0, num1, num0 + num1, sw.Elapsed.Seconds);

            Console.WriteLine("Good Times");
        }

        public static int GetNumber(int num)
        {
            Task.Delay(1000 * num).Wait();
            return num;
        }
    }
}


pretty straight forward, we add two numbers in this case 3 and 4 with a delay equal in seconds to the numbers, so it takes 7 seconds for our calculation to complete and then we get Good Times displayed to us.

that's exactly what we expected but did we really need to wait for our calculation to complete before seeing the "Good Times" Message, it really has nothing to do with the calculation.

Let's use the Task Parallel library to do better:

using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace Example01
{
    class Program
    {
        static void Main(string[] args)
        {
            var num0 = 3;
            var num1 = 4;

            var sw = new Stopwatch();
            sw.Start();

            Task T = Task.Run(() =>
            {
                Console.WriteLine("Sum TaskId:{0}", Task.CurrentId);
                return GetNumber(num0) + GetNumber(num1);
            }).ContinueWith(tr =>
            {
                sw.Stop();
                Console.WriteLine("\nContinueWith TaskId:{0} ", Task.CurrentId);
                Console.WriteLine("{0} + {1} = {2} in {3}'s", num0, num1, tr.Result, sw.Elapsed.Seconds);
            });

            Console.WriteLine("Good Times");
            Task.WaitAll(T);
        }


        public static int GetNumber(int num)
        {
            Console.WriteLine("GetNumber({1}) TaskId:{0} ", Task.CurrentId, num);
            Task.Delay(1000 * num).Wait();
            return num;
        }
    }
}


This time we print the "Good Times" message immediately and calculate our total in a separate task.


but as you can see we only have two tasks in this modification, one to get our numbers synchronously and add them up, then a second task to display our results.

We can do better lets start up two more Tasks inside of our sum task to get the numbers separately and shave some time off our calculation time.

using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace Example02
{
    class Program
    {
        static void Main(string[] args)
        {
            var num0 = 3;
            var num1 = 4;

            var sw = new Stopwatch();
            sw.Start();

           
            Task T = Task.Run(() =>
            {
                Console.WriteLine("Sum TaskId:{0}", Task.CurrentId);
                var t0 = Task<int>.Run(() => GetNumber(num0));
                var t1 = Task<int>.Run(() => GetNumber(num1));

                return t0.Result + t1.Result;
            }).ContinueWith(tr =>
            {
                sw.Stop();
                Console.WriteLine("\nContinueWith TaskId:{0} ", Task.CurrentId);
                Console.WriteLine("{0} + {1} = {2} in {3}'s", num0, num1, tr.Result, sw.Elapsed.Seconds);
            });

            Console.WriteLine("Good Times");
            Task.WaitAll(T);
        }


        public static int GetNumber(int num)
        {
            Console.WriteLine("GetNumber({1}) TaskId:{0} ", Task.CurrentId, num);
            Task.Delay(1000 * num).Wait();
            return num;
        }
    }
}

not too bad, we created a sum task that retrieves our two number in parallel using two more task, then adds them up and prints them to the screen in a fourth task. As before our "Good Times" message is printed to the screen instantly.

now lets try the same thing but using the async/await keywords.

using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace Example03
{
    class Program
    {
        static void Main(string[] args)
        {
            Action<int,int> calculate = async (num0, num1) => {
                var sw = new Stopwatch();
                sw.Start();

                int total = await Task.Run<int>(() => {
                    Console.WriteLine("Sum TaskId:{0}", Task.CurrentId);

                    var t0 = Task<int>.Run(() => GetNumber(num0));
                    var t1 = Task<int>.Run(() => GetNumber(num1));

                    return t0.Result + t1.Result;
                });

                sw.Stop();

                Console.WriteLine("{0} + {1} = {2} in {3}'s", num0, num1, total, sw.Elapsed.Seconds);
            };

            calculate(3, 4);

            Console.WriteLine("Good Times");
            Console.ReadKey();
        }

        public static int GetNumber(int num)
        {
            Console.WriteLine("GetNumber({1}) TaskId:{0} ", Task.CurrentId, num);
            Task.Delay(1000 * num).Wait();
            return num;
        }
    }
}


pretty straight forward, one thing that may through you off is the use of an action inside the main, this is because entry points cannot be asynchronous.

the above being a bit convoluted, here's a final example to really demonstrate that Task.Run().ContinueWith is the same thing as async/await

using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace Example04
{
    class Program
    {
        static void Main(string[] args)
        {
            var num0 = 3;
            var num1 = 4;
            var sw = new Stopwatch();

            sw.Start();
            var result0 = SlowAdd(num0, num1);
            sw.Stop();
            Console.WriteLine("TPL: {0} + {1} = {2} in {3}'s", num0, num1, result0, sw.Elapsed.Seconds);

            sw.Restart();
            var result1 = SlowAddAsync(num0, num1).Result;
            sw.Stop();
            Console.WriteLine("ASYNC: {0} + {1} = {2} in {3}'s", num0, num1, result1, sw.Elapsed.Seconds);
        }

        static int SlowAdd(int a, int b)
        {
            return GetNumber(3) + GetNumber(4);
        }

        async static Task<int> SlowAddAsync(int a, int b)
        {
            return await GetNumberAsync(a) + await GetNumberAsync(b);
        }

        static int GetNumber(int num)
        {
            //start a task
            return Task.Run(
                //wait the number * seconds
                () => Task.Delay(1000 * num))
                //once waiting task complete continue
                    .ContinueWith(
                //return the number
                        tr => num).Result;
        }

        async static Task<int> GetNumberAsync(int num)
        {
            await Task.Delay(1000 * num);
            return num;
        }
    }
}

above, the two functions to pay attention to are GetNumber and GetNumberAsync, they both do the same thing, they take in a number wait an equal amount of seconds then pass the number back. In fact they will compile down to roughly the same code it's just that one is much easier to read than the other.