Click here to Skip to main content
15,881,424 members
Articles / Web Development / ASP.NET
Article

Practical .NET2 and C#2 - ASP.NET 2.0: Handling a page request on several threads

Rate me:
Please Sign up or sign in to vote.
4.30/5 (5 votes)
28 Apr 20063 min read 46.3K   26   4
ASP.NET 2.0: Handling a page request on several threads.

TitlePractical .NET2 and C#2
AuthorPatrick Smacchia
PublisherParadoxalPress
PublishedJanuary 2006
ISBN0-9766132-2-0
PriceUSD 59.95 (ParadoxalPress price: USD 33.99)
Pages896
Websitewww.PracticalDOT.NET (Browse and download the 647 listings and sample chapters)

Introduction

The execution of a request within the aspnet_wp.exe process is done on the same thread from end to end. For this, ASP.NET uses the I/O threads of the CLR’s thread pool, and can execute multiple requests simultaneously. In addition to avoiding a certain number of concurrency problems by design, this approach is generally more efficient as the reception of requests from a name pipe starts directly on an I/O thread. We then avoid the transmission of information between threads. Since there is no named pipes in IIS 6.0, things happen a little differently, and in this version, ASP.NET uses several worker threads of the CLR.

It happens that the processing of a request implies a long wait for the thread. Typically, this happens when we access a slow web service, or when we complete an expensive request on a database. In this case, the ASP.NET threading model is inefficient. In fact, the wait of a thread is not without consequences, since the number of threads in the pool used to serve the request can be limited. If most requests imply a wait from their threads, the server will quickly find itself overloaded. We are then faced with a design bottleneck.

ASP.NET 2.0 offers an infrastructure to solve this problem. The idea is that the finalization of the processing of a request be accomplished on a different thread than the one that initiated the processing. Meanwhile, the access to a resource, such as a web service or a database, is done without holding a thread from the pool. In the case of a SQL Server database access, this is possible using the asynchronous requests of ADO.NET 2.0. This feature allows making a request to a SQL Server database without using a thread from the pool while waiting for the data. The following example illustrates a page which uses the feature, allowing ASP.NET 2.0 to process a page on multiple threads, and the feature of ADO.NET 2.0 allowing asynchronous requests. The data recovered from the database is then presented in a GridView:

ASP.NET
<%@ Page Language="C#" Async="true" 
         CodeFile="Default.aspx.cs" Inherits="_Default" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
   <body>
      <form id="form1" runat="server">
         <asp:GridView ID="MyGridView" runat="server" 
                  AutoGenerateColumns="true" />
      </form>
   </body>
</html>
C#
using System;
using System.Data;
using System.Data.SqlClient;
using System.Web;
using System.Web.UI;
using System.Threading;
public partial class _Default : System.Web.UI.Page {
   private SqlCommand cmd;
   private SqlConnection cnx;
   protected void Page_Load(object sender, EventArgs e) {
      Response.Write("PageLoad thread:"+ 
           Thread.CurrentThread.ManagedThreadId + 
           "<br/>");
      PageAsyncTask task = new PageAsyncTask( BeginInvoke, 
             EndInvoke, EndTimeOutInvoke, null);
      Page.RegisterAsyncTask(task);
   }
   public IAsyncResult BeginInvoke( object sender, 
                                    EventArgs e, 
                                    AsyncCallback callBack, 
                                    object extraData) {
      Response.Write( "BeginInvoke thread:" + 
         Thread.CurrentThread.ManagedThreadId + 
         "<br/>");
      cnx = new SqlConnection( "async=true ; server" + 
            " = localhost ; " + "uid=sa ; pwd =; " + 
            "database = ORGANIZATION");
      cmd = new SqlCommand( "SELECT * FROM EMPLOYEES", cnx );
      cnx.Open();
      return cmd.BeginExecuteReader( callBack, extraData, 
                         CommandBehavior.CloseConnection);
   }
   public void EndInvoke( IAsyncResult result ) {
      Response.Write( "EndInvoke thread:" + 
        Thread.CurrentThread.ManagedThreadId + "<br/>");
      SqlDataReader rdr = cmd.EndExecuteReader(result);
      if (rdr != null && rdr.HasRows) {
         MyGridView.DataSource = rdr;
         MyGridView.DataBind();
      }
      rdr.Close();
   }
   public void EndTimeOutInvoke( IAsyncResult result ) {
      if (cnx != null && cnx.State != ConnectionState.Closed) {
         cnx.Close();
      }
      Response.Write("TimeOut");
      Response.End();
   }
   protected override void OnPreRender( EventArgs e ) {
      Response.Write( "OnPreRender thread:" + 
        Thread.CurrentThread.ManagedThreadId + 
        "<br/>");
   }
   protected override void OnPreRenderComplete( EventArgs e ) {
      Response.Write( "OnPreRenderComplete thread:" + 
        Thread.CurrentThread.ManagedThreadId + 
        "<br/>");
   }
}

To support this multi-threaded processing model, you must set the Async property of the <%@ Page> directive to true. During the Load event, we can create instances of the System.Web.UI.PageAsyncTask class. Each instance represents a processing which will occur outside of the aspnet_wp.exe process. No thread from the pool is used to wait for the end of the processing. This processing is represented by three delegates:

  • A delegate to the method that will begin the processing of the request (i.e., the one which executes Page_Load()). In our example, the delegate references the BeginInvoke(). It is executed right after the PreRender() event.
  • A delegate to the method which completes the processing of the work on a thread of the pool once the work is completed. In our example, our delegate references the BeginInvoke method. It is executed right before the PreRenderComplete event.
  • A delegate to a method executed by a thread of the pool when the work was not completed within a certain amount of time. In this case, our delegate references the EndTimeOutInvoke() method.

The HTML page generated by this example starts as follows:

PageLoad thread:4
OnPreRender thread:4
BeginInvoke thread:4
EndInvoke thread:8
OnPreRenderComplete thread:8
...

If you wish to use this technique to asynchronously process a call to a web service, you can use the IAsyncResult BeginXXX() and EndXXX(IAsyncResult) methods of the proxy class generated by the wsdl.exe tool.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
France France
Patrick Smacchia is a .NET MVP involved in software development for over 15 years. He is the author of Practical .NET2 and C#2, a .NET book conceived from real world experience with 647 compilable code listings. After graduating in mathematics and computer science, he has worked on software in a variety of fields including stock exchange at Société Générale, airline ticket reservation system at Amadeus as well as a satellite base station at Alcatel. He's currently a software consultant and trainer on .NET technologies as well as the author of the freeware NDepend which provides numerous metrics and caveats on any compiled .NET application.

Comments and Discussions

 
GeneralCoïncidences Pin
Olivier DALET2-Oct-07 23:36
Olivier DALET2-Oct-07 23:36 
GeneralMultiple Calls Pin
IshfaqHussain4-Sep-07 2:48
IshfaqHussain4-Sep-07 2:48 
GeneralThanks :) Pin
dapoussin13-Oct-06 3:55
dapoussin13-Oct-06 3:55 
GeneralVery useful!! Pin
Jon Rista29-Apr-06 1:42
Jon Rista29-Apr-06 1:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.