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.

Monday, 21 July 2014

k2 Custom SmartControl: Client Side

This is a continuation of the previous three posts:

To set up a k2 Custom Control Visual Studio project click here.
To set up the xml definition click here.
To set up the server side class click here.

if you have these three complete continue on.

Picking up where we left off we have to make one last modification, recall that we added a reference to our javascript file in the AssemblyInfromation.cs class, but we still have to add our JavaScript to the rendered page in the Constructor of our Custom Control. so lets do that:

public MySmartControl()
    : base()
{
    //Define the javascript key and path
    string jsKey = "MyCustomControls_MySmartControl_MySmartControl_Script";
    string jsUrl = "MyCustomControls.MySmartControl.MySmartControl_Script.js";

    // add the script to the designer environment
    this.DesignCodePaths.Add(jsKey, jsUrl);

    // add the script to the runtime environment
    this.CodePaths.Add(jsKey, jsUrl);
}

now we can focus on JavaScript, so lets add functions to get our control, set and get both the value and properties. First we'll start with an empty self calling function that builds our javascript object

// self calling function, will fire itself when rendered
(function ($, undefined) {

    //check if MyCustomControls object exists, if not create it
    if (typeof MyCustomControls === "undefined" || MyCustomControls === null)
        MyCustomControls = {};

        //add MySmartControl object to MyCustomControls object
        MyCustomControls.MySmartControl = {
        
        //TODO add interaction logic between control and xml definition 
      
        };
}(jQuery));


with this ready now we have to implement all the endpoints we defined in our XML Definition, namely

  • get/setValue
  • get/setProperty
  • events

// self calling function, will fire itself when rendered
(function ($, undefined) {

    //check if MyCustomControls object exists, if not create it
    if (typeof MyCustomControls === "undefined" || MyCustomControls === null)
        MyCustomControls = {};

        //add MySmartControl object to MyCustomControls object
        MyCustomControls.MySmartControl = {

            // Custom function to retrieve html rendering of control, it is marked
            // with an underscore to emphesize that it is a private function that is
            // not declared in the xml definition
            _getControl: function (objInfo)
            {
                return document.getElementById(objInfo.CurrentControlId);
            },

            // getValue function defined in xml Definition
            getValue: function (objInfo) {
                var control = MyCustomControls.MySmartControl._getControl(objInfo);
                return control.innerHTML;
            },

            // setValue method defined in xml Definition
            setValue: function (objInfo) {
                var control = MyCustomControls.MySmartControl._getControl(objInfo);
                control.innerHTML = objInfo.Value;
            },

            // getProperty method defined in xml Definition, retrieves property
            // that is saved as attribute on the root container of the control
            getProperty: function (objInfo) {
                var control = MyCustomControls.MySmartControl._getControl(objInfo);
         
                if (control.hasAttribute(objInfo.property))
                    return control.getAttribute(objInfo.property);
                return control.getAttribute("data-{0}".format(objInfo.property));
            },

            // setProperty method defined in xml Definition, sets an attribute on
            // the root element of the control to the property value
            setProperty: function (objInfo) {
                var control = MyCustomControls.MySmartControl._getControl(objInfo);

                if (control.hasAttribute(objInfo.property))
                    return control.setAttribute(objInfo.property, objInfo.Value);
                control.setAttribute("data-{0}".format(objInfo.property), objInfo.Value);
            },

            // excute is defined in the xml Definition, it is responsible for handling
            // all incomeing method calls that are defined in the xml definition and called
            // from the k2 rules.
            execute: function (objInfo) {
                var parameters = objInfo.methodParameters;
                var method = objInfo.methodName;

                switch (method) {
                    case "ShowAlert":
                        alert(parameters.message);
                        return;
                    case "Add":
                        return (parameters.double ? 2 : 1) *
                               (parseInt(parameters.addendone) + parseInt(parameters.addendtwo));
                }
            }
        };

}(jQuery));

now with that complete we have to add the logic to pass on the events from the control to k2

$().ready(function () {
    $(document).delegate('.MyCustomControls-MySmartControl', 'click.Control', function (e)
    {
        console.log(this.id); //Notice that this.id = CurrentControlId

        // raiseEvent is an internal k2 fucntion in the events.js file that will pass the
        // event to the k2 rules and fire the event on the appropriate instance of the control
        raiseEvent(this.id, 'Control', 'OnClick');
    });

});

To recap: Think of your basic control as a three file beast:

  • Xml Definition: used to define all the properties, methods, events and supporting functions your control will have. The properties are mapped to the server side .cs file for serialization, but the methods, events and supporting functions are defined in the client side .js file.
  • Server side: used to de/serialize all the properties defined in your control, also to render the control initially on the page in the RenderControl method.
  • Client side: the meat of the control, this is where the interaction between the user and k2 environment occurs, events let users execute k2 rules and methods let k2 rules execute functions defined in the client side of your control.