Running an asynchronous operation triggered by an ASP.NET web page request Running an asynchronous operation triggered by an ASP.NET web page request asp.net asp.net

Running an asynchronous operation triggered by an ASP.NET web page request


If you are running webforms set Ansync = "true" in your .aspx page where you are making the request.
<%@ Page Language="C#" Async="true" ... %>


If you don't care about returning anything to the user, you can just fire up either a separate thread, or for a quick and dirty approach, use a delegate and invoke it asynchrnously. If you don't care about notifying the user when the async task finishes, you can ignore the callback. Try putting a breakpoint at the end of the SomeVeryLongAction() method, and you'll see that it finishes running after the page has already been served up:

private delegate void DoStuff(); //delegate for the actionprotected void Page_Load(object sender, EventArgs e){}protected void Button1_Click(object sender, EventArgs e){    //create the delegate    DoStuff myAction = new DoStuff(SomeVeryLongAction);     //invoke it asynchrnously, control passes to next statement    myAction.BeginInvoke(null, null);    Button1.Text = DateTime.Now.ToString();}private void SomeVeryLongAction(){    for (int i = 0; i < 100; i++)    {        //simulation of some VERY long job        System.Threading.Thread.Sleep(100);    }}


OK, here's the problem: the Async attribute is for the case where your page is going to call some long-running task that also blocks the thread, and then your page needs the output from that task in order to return info to the user. For example, if your page needed to call a web service, wait for its response, and then use the data from the response to render your page.

The reason you'd use the Async attribute is to avoid blocking the thread. This is important because ASP.NET applications use a thread pool to serve requests, and there are only a relatively small number of threads available. And if each call ties up the thread while waiting on the web service call, then soon you're going to hit enough concurrent users that users are going to have to wait until these web service calls complete. The Async attribute lets the thread return to the thread pool and serve other concurrent visitors to your web site, rather than forcing it to sit still doing nothing while waiting for the web service call to return.

The upshot for you is this: the Async attribute is designed for the case where you can't render the page until the asynchronous task completes, and that's why it doesn't render the page immediately.

You need to launch your own thread, and make it a daemon thread. I don't remember the exact syntax for that, but you can easily find it in the doc by searching the BCL doc for "daemon". This means the thread will keep your application from shutting down while it is alive, which is important because ASP.NET and IIS reserve the right to "recycle your process" when they deem it necessary, and if that happens while your thread is working, your task will be stopped. Making the thread daemon will prevent this (except for some possible rare edge cases ... you'll find out more when you find the documentation on this).

That daemon thread is where you will kick off these tasks. And after you've told the daemon thread to do the task, you can immediately render your page ... so the rendering of the page will happen immediately.

Even better than a daemon thread in your ASP.NET process, though, would be to implement a Windows Service for doing the task. Have your ASP.NET application communicate the task to be performed to the Service. No need for a daemon thread and no need to worry about your ASP.NET process being recycled. How do you tell the Service to do the task? Perhaps through WCF, or perhaps by inserting a record into a database table that the Service polls. Or a number of other ways.

EDIT: Here's another idea, which I have used before for this very same purpose. Write the info about your task into an MSMQ queue. Have another process (maybe even on another machine) pull from that queue and do the time-consuming task. The job of inserting into a Queue is optimized to return as quickly as possible, so your thread won't block while the data you put in the Queue is sent across the wire or anything like that. It is one of the fastest ways to make note of the fact that a task needs to be done without waiting for that task to execute.