Executing async tasks in Asp.net
Thursday, October 23 2008 - asp-net, async-task, iasyncresult, registerasynctask
In this article I want to show 3 different ways to deal with long running tasks in Asp.net.
If such a task must be initiated from an Asp.net web page, the thread that is serving the page request is bound to the task until it completes or a timeout occurs. Asp.net and .net framework offer different ways to address this issue, in the project used for this article, a sample page attempts to star a long running task in three different ways:
1. Synchronously simply starting the task and waiting for its completion
2. Asynchronously, using Page.RegisterAsyncTask and other features
3. Asynchronously, using asynchronous delegates
Each of these options have pro and cons, and fit in different scenarios, but also these options don't require to include the Async="true" attribute in the page's @ Page directive.
The first option entails to keep busy the thread that is serving the request, until the task completes. This could be a bottleneck, especially if multiple users start multiple tasks. This condition will probably end up to a 503 "Server Unavailable" error.
It's advisable to start a new thread that will perform the task, allowing the first thread to return in the thread pool and stand-by to serve a new incoming request.
To reach our goal, no modifications are required to the actual codebase that is in charge to execute the task. In the second option we are going to use a Page's method that first registers an async task, passing three different event handlers: one to begin/launch the task, one to manage its completion and the last one used to recover from a timeout that may occur during the task execution.
This option also uses IAsyncResult interface described here, but no extra coding is needed to use it, take a look to the code sample.
The SimpleWorker class, exposes one method that simulates a long running operation but it only writes a text lines into a log file and waits one second per each line. Imagine an orders fulfillment procedure that creates packing lists and send an email to customers indicating that their orders are "approved".
1: PageAsyncTask task = new PageAsyncTask(OnBegin, OnEnd, OnTimeout, null);
2: Page.RegisterAsyncTask(task);
3:
4: TraceStatusInfo("TEST CASE #2: Start a long running task asynchronously using PageAsyncTask.");
5:
6: //Setting up a 5 secs timeout
7: Page.AsyncTimeout = new TimeSpan(0, 0, 5);
8:
9: TraceStatusInfo("Starting task");
10:
11: Page.ExecuteRegisteredAsyncTasks();
Whether such a procedure could be started from an Asp.net page, is a good practice to maintain an activity registry or log, because the web application could be suddenly stopped without any warning and the worker process may be recycled due to an excessive, unexpected resource consumption.
The third option is the simplest one, it just uses an asynchronous delegate to start the task before sending the response to the client. When the client browser renders the page after the postback, the task is still running into another thread on the server and the original thread, that initially served the request, is now free and returned in the thread pool. You could look at the ThreadID into the SimpleWorker's log file and compare the server time when logging entries were added.
1: TraceStatusInfo("TEST CASE #3: Start a long running task asynchronously using an asynchronous delegate.");
2: SimpleWorker w=new SimpleWorker();
3: atd2 = new AsyncTaskDelegate(w.DoWork);
4:
5: TraceStatusInfo("Starting task");
6:
7: atd2.BeginInvoke(int.Parse(txtUnits.Text), null, null);
8:
9: litStatus.Text = "Test #3 completed.";
The difference between the two last approaches, is on the fact that the former can get the task result as it completes and inform the user, the latter must poll the server to see what had happened, this should not be an issue for some scenarios, however an Ajax script could periodically hit the server querying an activity registry, like a table or a file, until the task is successfully completed. The second option could also benefit of the timeout handler, the third option instead is best suited for fire and forget tasks.
Here is a snapshot of the log file in which you can see that TreadID changes when an async task is executed in tests cases #2 and 3#.
Code download
Further readings
[1] MSDN Magazine - Scalable Apps with Asynchronous Programming in ASP.NET
[2] MSDN Magazine - Asynchronous Pages in ASP.NET 2.0
[3] MSDN Magazine - Build Concurrent Apps From Simple F# Expressions
6 comment(s)
Hi Don,
thanks.
Yes method #3 also supports a callback, but in the example the page is rendered to the client browser immediately after the "fire and forget" call, without waiting for task completion.
Method #2, is best suited for scenarios in which you need more control on async task and also the opportunity to recover if a timeout occurs.
Hi,
what about ASP.NET MVC?
thanks
thank you Andrea,
thank you very much,
i stuck in a project for a month for this thread thinks,
after reading your articles,
i solve it and its working,
Thanks Andrea:)
I am sure this doen't requires to set Page Directives
Yes, you're right:
"...but also these options don't require to include the Async="true" attribute in the page's @ Page directive."


Great article. But I believe that the asynchronous delegate method (#3) also supports a callback method. You don't have to use #2 to get that feature.