About the author

Brian Keating is a developer addicted to Microsoft Technologies.

Month List

RecentComments

Comment RSS

Async MVC Controllers

clock March 14, 2012 10:26 by author Brian Keating |

 

I previously wrote a post on MVC async controllers. Now I want to follow up on something that’s more cutting edge, unfortunately I’m one of those people that like nosing about with what new, how it will affect me and how I could leverage it in future etc., Often, I’ll readily admit it’s more bleeding edge than cutting edge (xaml support in Blend5 for example Thumbs down).

However in this post I want to show you something that you might agree is pretty nice.

A historical example

So lets take a look at the app we’re trying to build.

Client is a simple web form (yes it’s webforms but I’m trying to catch the non MVC microsofties too), the instruments are entered in the text box, fetch button is clicked and the result is output

image_thumb1

Now the workflow, for the purpose of this test I won’t be connecting to any database webservice etc, I’ve just added a delay of 10 seconds and and assign LastClose to RIC & " " & New Random().NextDouble()

image_thumb4

Here’s the code behind for the non-async button event handler

        protected void Button1_Click(object sender, EventArgs e)
        {
            Contract.Requires<ArgumentException>(!string.IsNullOrWhiteSpace(tbInst1.Text) || !string.IsNullOrWhiteSpace(tbInst2.Text), "Please enter values for instruments");
            // Make the call to the workflow to get the close for each instrument
            try
            {
                var args = new Dictionary<string, object>();
                args.Add("RIC", tbInst1.Text);                
                var res = WorkflowInvoker.Invoke(_getPricesWFDefinition, args);
                Label1.Text = res["LastClose"].ToString();
 
                args.Clear();
                args.Add("RIC", tbInst2.Text);
                res = WorkflowInvoker.Invoke(_getPricesWFDefinition, args);
                Label2.Text = res["LastClose"].ToString();
            }
            catch (Exception exp)
            {
                Trace.Warn(exp.Message);
            }
 
        }

So as you can see, we’re waiting at least 20 seconds for our page to return, nasty.
In theory we should be able to bring this down to 10 seconds as we can make the calls to the workflows in parallel.

Let me show you one way to “incorrectly” achieve this

        private void Option1()
        {
 
            var t1 = Option1TaskCreater(tbInst1, Label1);
            var t2 = Option1TaskCreater(tbInst2, Label2);
 
            Task.WaitAll(t1, t2);
 
        }
 
        private Task Option1TaskCreater(TextBox tb, Label lb)
        {
            var t1 = Task.Factory.StartNew(() =>
            {
                var args = new Dictionary<string, object>();
                args.Add("RIC", tb.Text);
                var res = WorkflowInvoker.Invoke(_getPricesWFDefinition, args);
                lb.Text = res["LastClose"].ToString();
            });
            return t1;
        } 

So we’re using the task library to make the two workflow requests async, and results look promising, down to about 10 seconds now… so any problems with doing this?

Well, tasks use threadpool threads to execute. So, the query will execute on a threadpool thread. To get true asynchronous execution(no worker threads blocked), we need to jump through a few more hoops. For webform people, there’s a very good explanation here. MVC people read on!

But Why/When use async?

The response to this question has been discussed many times in both books and magazines. ASP.NET uses threads from a common language runtime (CLR) thread pool to process requests. As long as there are threads available in the thread pool, ASP.NET has no trouble dispatching incoming requests. But once the thread pool becomes saturated-that is, all the threads inside it are busy processing requests and no free threads remain-new requests have to wait for threads to become free. If the jam becomes severe enough and the queue fills to capacity, ASP.NET throws up its hands and responds with a "Server Unavailable" to new requests.

Often to solve the problem of horizontal scalability more servers are added to the webfarm, however this only provided temporary relief to what in fact is an underlying design problem, it’s not a case of adding more processors(threads) but a case of using the threads more efficiently, as you can see from the diagram below, if you are CPU bound, there is little more you can do but add some more servers.

image_thumb2

So what is this telling us? Basically, if your app is I/O bound then you should use parallelism, if requests are computationally cheap to process, then parallelism is probably an unnecessary overhead.

If the incoming request rate is high, then adding more parallelism will likely yield few benefits and could actually decrease performance, since the incoming rate of work may be high enough to keep the CPUs busy.

If the incoming request rate is low, then the Web application could benefit from parallelism by using the idle CPU cycles to speed up the processing of an individual request. We can use either PLINQ or TPL (either Parallel loops or the Task class) to parallelize the computation over all the processors. Note that by default, however, the PLINQ implementation in .NET 4 will tie-up one ThreadPool worker per processor for the entire execution of the query. As such, it should only be used in Web applications that see few but expensive requests.

MVC4

In MVC4 it becomes even easier that my previous post on AsyncControllers, actually to me it looks quite like the WinRT async calls in .net 4.5 and C#5

Sample using task support for asynchronous controllers

You can now write asynchronous action methods as single methods that return an object of type Task or Task<ActionResult>.

e.g.

public async Task<ActionResult> Index(string city)
{    
    var newsService = new NewsService();    
    var sportsService = new SportsService();        
    return View("Common",        
                new PortalViewModel 
                {       
                    NewsHeadlines = await newsService.GetHeadlinesAsync(),        
                    SportsScores = await sportsService.GetScoresAsync()    
                });
}

In the previous action method, the calls to newsService.GetHeadlinesAsync andsportsService.GetScoresAsync are called asynchronously and do not block a thread from the thread pool.

Asynchronous action methods that return Task instances can also support timeouts. To make your action method cancellable, add a parameter of type CancellationToken to the action method signature. The following example shows an asynchronous action method that has a timeout of 2500 milliseconds and that displays a TimedOut view to the client if a timeout occurs.

[AsyncTimeout(2500)] 
[HandleError(ExceptionType = typeof(TaskCanceledException), View = "TimedOut")]
public async Task<ActionResult> Index(string city, CancellationToken cancellationToken) 
{    
    var newsService = new NewsService();    
    var sportsService = new SportsService();       
    return View("Common",        
        new PortalViewModel 
        {        
            NewsHeadlines = await newsService.GetHeadlinesAsync(cancellationToken),        
            SportsScores = await sportsService.GetScoresAsync(cancellationToken)    
        });
}
 
 
 

Conculsion

MVC4 Makes programming of async controllers even easier.




WF4 WriteLine testing using extensions

clock December 17, 2010 21:37 by author Brian Keating |

If you wish to test specific writelines on a console application: you can do so by adding a StringWriter object to the workflow extensions.
The writeline activity will use the textwriter if it exists as opposed to the console.

[TestMethod]
public void CheckWorkflowWriteLine()
{   
 var wi = new WorkflowInvoker(new HelloWorld());

        //Add string writer to extensions  
 var writer = new StringWriter();    
 wi.Extensions.Add(writer);    
 wi.Invoke();    
 Assert.AreEqual("Hello Workflow", writer.ToString());
}




Windows Workflow 4.0

clock November 2, 2009 07:34 by author Brian Keating |

Ok, forget what you learned about workflow in 3.5.

Windows Workflow 4.0

4.0 changes everything you thought you knew about workflow.

WF 4.0 represents a bottom-up rewrite with entirely new thinking. The gains are enormous: custom activities take center stage, and authoring them is much simpler; workflows are entirely declarative; and there are three workflow flow styles that you can combine seamlessly. It's possible that you could see a 10-fold improvement in the time required to create and debug workflows, in addition to 10- to 100-fold runtime performance improvements. You'd also have better control over persistence. Additionally, there's support for partial trust, a completely rewritten designer, and a better debugging experience. One of the most important improvements is in the re-hosting experience. This allows you to create activities and to allow power users -- and others -- to build or modify workflows with the constrained set of activities you've created. Yes, this is possible in WF 3.0/3.5, but the necessary code is challenging, not to mention exceedingly difficult to debug.

The improvements to WF 4.0 come at a cost. Microsoft developed two inter-op strategies to help you over this hurdle. WF 3.0/3.5 will remain part of the framework and will run side by side with WF 4.0. This lets you manage the transition at a time that fits your organization's broader goals. The other inter-op strategy is an inter-op activity that allows you to use WF 3.0/3.5 activities in a WF 4.0 workflow. Workflows are a specialization of activities, so you'll probably be able to run entire 3.0/3.5 workflows inside WF 4.0 workflows.

 

Screenshot VS2010 B2