線程,是操作系統中的術語,是操作系統進行運算調度的最小單位,它被包含在進程之中,是進程中的實際運作單位。一個進程可以有很多線程,每條線程並行執行不同的任務。同一進程中的多條線程將共享該進程中的全部系統資源,如虛擬地址空間,文件描述符和信號處理等等。但同一進程中的多個線程有各自的調用棧(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。待續。。。。
