ASP.NET中的多線程整理


   線程,是操作系統中的術語,是操作系統進行運算調度的最小單位,它被包含在進程之中,是進程中的實際運作單位。一個進程可以有很多線程,每條線程並行執行不同的任務。同一進程中的多條線程將共享該進程中的全部系統資源,如虛擬地址空間,文件描述符和信號處理等等。但同一進程中的多個線程有各自的調用棧(call stack),自己的寄存器環境(register context),自己的線程本地存儲(thread-local storage)。我們把用來執行用戶任務的線程稱為工作線程。而線程池,是一種成熟的線程使用模式。

  為什么要創建線程池?

  線程池屬於對象池.所有對象池都具有一個非常重要的共性,就是為了最大程度復用對象.那么,線程池的最重要的特征也就是最大程度利用線程。所以線程池的目的就是為了減少創建和切換線程的額外開銷,利用已經的線程多次循環執行多個任務從而提高系統的處理能力.

  在ASP.NET工作進程中有兩種線程池,Worker線程池處理所有傳入的請求, I / O線程池處理的I / O(訪問文件系統,Web服務和數據庫等)。每個應用程序域都有其自己的線程池,可以排隊到線程池的操作的數量只受可用內存的限制,然而,對線程池中的線程數的限制在這個過程中可以同時被激活。

  當我們發出一個(異步)頁面請求。一個Worker線程就會被從Worker線程池中取出, 這個Worker線程會觸發I/O操作。當I/O操作開始時,另一個線程將會被從I/O線程池中取出,在收到I/O線程的返回值之前,Worker線程會一直處於閑置狀態。所以,如果你的頁面加載事件觸發了多個I/O操作,那么,Worker線程就很可能會被閑置很長時間。而線程池能夠把正處於閑置狀態的Worker線程回收,使他能夠為其他的頁面請求提供服務。從而,降低系統開銷。

只要並發請求的數量不超過線程池的可用線程的數量,一切都很好。但是,當你正在構建企業級應用程序時,你的線程池中的線程的數量就會達到極限。當這種情況發生時,新的請求會進入請求隊列。 而ASP.NET支持在它開始拒絕請求並返回錯誤503服務不可用之前的最大請求數量,但是,我還沒搞清楚這個具體的數量值是多少。

以下為多線程示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;
using System.Threading;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;

namespace AsyncWeb
{
    public partial class ThreadAsync : System.Web.UI.Page
    {
        private string connect = ConfigurationManager.ConnectionStrings["ValvesTestConnectionString"].ConnectionString.ToString();

        private DataTable threadOneResult;
        private DataTable threadTwoResult;
        private DataTable threadThreeResult;

        protected void Page_Load(object sender, EventArgs e)
        {
            AsyncCallsWithThreadPool();
        }

        public void AsyncCallsWithThreadPool()
        {
            ThreadPool.QueueUserWorkItem(ThreadOne, 1000);
            ThreadPool.QueueUserWorkItem(ThreadTwo, 5000);
            ThreadPool.QueueUserWorkItem(ThreadThree, 10000);

            while (threadOneResult == null ||
                threadTwoResult == null ||
                threadThreeResult == null)
            {
                Thread.Sleep(10000);
            }

            // continue
            threadOneResult.Merge(threadTwoResult);
            threadOneResult.Merge(threadThreeResult);
            Output.DataSource = threadOneResult;
            Output.DataBind();
        }

        private void ThreadOne(object state)
        {
            DataSet ds = new DataSet();
            using (SqlConnection conn = new SqlConnection(connect))
            {
                SqlCommand cmd = new SqlCommand("SELECT top 10 header_id, line_id, ordered_item FROM oe_order_lines_all", conn);
                SqlDataAdapter da = new SqlDataAdapter(cmd);
                da.Fill(ds);
            }
            threadOneResult = ds.Tables[0];
        }

        private void ThreadTwo(object state)
        {
            DataSet ds = new DataSet();
            using (SqlConnection conn = new SqlConnection(connect))
            {
                SqlCommand cmd = new SqlCommand("SELECT top 20 header_id, line_id, ordered_item FROM oe_order_lines_all", conn);
                SqlDataAdapter da = new SqlDataAdapter(cmd);
                da.Fill(ds);
            }
            threadTwoResult = ds.Tables[0];
        }

        private void ThreadThree(object state)
        {
            DataSet ds = new DataSet();
            using (SqlConnection conn = new SqlConnection(connect))
            {
                SqlCommand cmd = new SqlCommand("SELECT top 30 header_id, line_id, ordered_item FROM oe_order_lines_all", conn);
                SqlDataAdapter da = new SqlDataAdapter(cmd);
                da.Fill(ds);
            }
            threadThreeResult = ds.Tables[0];
        }
    }
}

在這個例子中,我在線程池中放了三個線程。當程序啟動時,這三個線程就會被從線程池中取出,然后分別執行對應的操作,如從數據庫中取數據。這三個線程在執行時,主線程會處於閑置狀態,直到這三個線程完成主線程才會繼續執行。我這里是使用While循環來實現的,其實還可以用其他方式實現。使用這種方式對線程的控制程度比較高,要特別小心的使用。ASP.NET也為開發人員提供了它自己的異步編程模型,一起來看看吧!

 

ASP.NET 中的異步編程模型

ASP.NET提供的異步編程模型包含:異步頁(Asynchronous Page),Asynchronous Module 和 Asynchronous WebService。使用Asynchronous WebService實現異步的方式比較普遍,技術文檔比較多。所以,本篇就不對Asynchronous WebService進行介紹。好了,開始吧!

異步頁(Asynchronous Page)

  上圖為普通頁面和異步頁面的生命周期事件。相對於普通的頁面生存周期事件,異步頁多了異步事件,上圖綠色的部分就是異步事件在頁面事件中的執行位置。我們可以很清楚的看到主線程被一分為二了,其實這里主線程還是在的,只不過被閑置了來等待異步線程的執行結束。開發人員可以通過在ASP.NET中注冊(Begin,End)方法來實現異步操作。ASP.NET會調用Begin方法,在begin方法中去執行I/O操作,如執行數據庫查詢操作,ASP.NET將立即從Begin方法中返回而不會去等待返回值。ASP.NET只要檢測到線程返回了,就會立即把這個線程放回到線程池中,然后,使用這個線程去處理其他的請求。

  從Begin方法中返回的是一個IAsyncResult 接口,正是通過這個接口ASP.NET才知道Begin方法什么時候結束。當Begin方法結束時,ASP.NET會到線程池中取出另一個線程去調用end方法。在end方法中,我們可以對返回值進行處理。

  從ASP.NET的角度看,這只是一個普通的請求,但是確由兩個線程來執行。這會不會有什么問題呢?

  創建異步頁的步驟很簡單,首先在.aspx文件加入Async=”True”,目的就是程序在Runtime時告訴ASP.NET這是個異步頁。如果是處理數據的異步請求,要在Connection String 中加“Asynchronous Processing=true;”。

如下示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;

namespace AsyncWeb
{
    public partial class About : System.Web.UI.Page
    {
        private SqlConnection _connection;
        private SqlCommand _command;
        private SqlDataReader _reader;

        protected void Page_Load(object sender, EventArgs e)
        {
            PageAsyncTask task = new PageAsyncTask(BeginAsyncOperation,
            EndAsyncOperation,
            TimeoutAsyncOperation,
            null);

            RegisterAsyncTask(task); 
        }
        IAsyncResult BeginAsyncOperation(object sender, EventArgs e,
        AsyncCallback cb, object state)
        {
            string connect = ConfigurationManager.ConnectionStrings["ValvesTestConnectionString"].ConnectionString.ToString();
            _connection = new SqlConnection(connect);

            _connection.Open();
            _command = new SqlCommand(
                "SELECT top 10 header_id, line_id, ordered_item FROM oe_order_lines_all", _connection);
            return _command.BeginExecuteReader(cb, state);
        }

        void EndAsyncOperation(IAsyncResult ar)
        {
            _reader = _command.EndExecuteReader(ar);
            DataTable dt = new DataTable();
            dt.Load(_reader);
            Output.DataSource = dt;
            Output.DataBind();
        }

        void TimeoutAsyncOperation(IAsyncResult ar)
        {

            // Called if async operation times out (@ Page AsyncTimeout)

            Label1.Text = "Data temporarily unavailable";

        }    
    }
    
}

這是一個比較簡單的例子。但是,ASP.NET實現異步頁的步驟還是比較復雜的,至少比普通的頁復雜很多。但是,從性能上考慮,新請求能夠很快進入管道,並且不需要在應用程序請求隊列中等待很長的時間,這樣就使得整個應用程序的性能都得到了提高。

 

ASP.NET異步HttpHandlers

ASP.NET中第二種異步模式是使用HttpHandler, httphander通過文件的后綴名來確定請求類型然后再確定使用哪個處理方法。ASPX handler 用來處理來自.aspx頁面的請求。, 簡單的說,其實它就是一個實現了IHttpHandler接口的類,它包括IsReusable方法和 ProcessRequest方法。 ProcessRequest方法的主要工作就是處理HttpRequest並把它轉化為HttpResponse, 再將HttpContext傳到page中。

// Name this C# file HandlerTest.cs and compile it with the
// command line: csc /t:library /r:System.Web.dll HandlerTest.cs.
// Copy HandlerTest.dll to your \bin directory.

using System.Web;

namespace HandlerExample
{
   public class MyHttpHandler : IHttpHandler
   {
      // Override the ProcessRequest method.
      public void ProcessRequest(HttpContext context)
      {
         context.Response.Write("<H1>This is an HttpHandler Test.</H1>");      
         context.Response.Write("<p>Your Browser:</p>");
         context.Response.Write("Type: " + context.Request.Browser.Type + "<br>");
         context.Response.Write("Version: " + context.Request.Browser.Version);
      }

      // Override the IsReusable property.
      public bool IsReusable
      {
         get { return true; }
      }
   }
}

/*
______________________________________________________________

To use this handler, include the following lines in a Web.config file.

<configuration>
   <system.web>
      <httpHandlers>
         <add verb="*" path="handler.aspx" type="HandlerExample.MyHttpHandler,HandlerTest"/>
      </httpHandlers>
   </system.web>
</configuration>
*/


開發人員可以使用IHttpAsyncHandler來實現異步的HttpHandler。待續。。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM