成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

詳細(xì)解讀ASP.NET的異步

開發(fā) 后端
在前文中,介紹了.NET下的多種異步的形式,在WEB程序中,天生就是多線程的,因此使用異步應(yīng)該更為謹(jǐn)慎。本文將著重展開ASP.NET中的異步。

在前文中,介紹了.NET下的多種異步的形式,在WEB程序中,天生就是多線程的,因此使用異步應(yīng)該更為謹(jǐn)慎。本文將著重展開ASP.NET中的異步。

【注意】本文中提到的異步指的是服務(wù)器端異步,而非客戶端異步(Ajax)。

對(duì)于HTTP的請(qǐng)求響應(yīng)模型,服務(wù)器無(wú)法主動(dòng)通知或回調(diào)客戶端,當(dāng)客戶端發(fā)起一個(gè)請(qǐng)求后,必須保持連接等待服務(wù)器的返回結(jié)果,才能繼續(xù)處理,因此,對(duì)于客戶端來(lái)說,請(qǐng)求與響應(yīng)是無(wú)法異步進(jìn)行,也就是說無(wú)論服務(wù)器如何處理請(qǐng)求,對(duì)于客戶端來(lái)說沒有任何差別。

那么ASP.NET異步指的又是什么,解決了什么問題呢?

在解釋ASP.NET異步前,先來(lái)考察下ASP.NET線程模型。

ASP.NET線程模型

我們知道,一個(gè)WEB服務(wù)可以同時(shí)服務(wù)器多個(gè)用戶,我們可以想象一下,WEB程序應(yīng)該運(yùn)行于多線程環(huán)境中,對(duì)于運(yùn)行WEB程序的線程,我們可以稱之為WEB線程,那么,先來(lái)看看WEB線程長(zhǎng)什么樣子吧。

我們可以用一個(gè)HttpHandler輸出一些內(nèi)容。

  1. public class Handler : IHttpHandler  
  2. {  
  3.  
  4.     public void ProcessRequest(HttpContext context)  
  5.     {  
  6.         context.Response.ContentType = "text/plain";  
  7.         var thread = Thread.CurrentThread;  
  8.         context.Response.Write(  
  9.             string.Format("Name:{0}\r\nManagedThreadId:{1}\r\nIsBackground:{2}\r\nIsThreadPoolThread:{3}",   
  10.                 thread.Name,  
  11.                 thread.ManagedThreadId,  
  12.                 thread.IsBackground,  
  13.                 thread.IsThreadPoolThread)  
  14.             );  
  15.     }  
  16.  
  17.     public bool IsReusable  
  18.     {  
  19.         get {return true;}  
  20.     }  

你可以看到類似于這樣的結(jié)果:

Name:

ManagedThreadId:57

IsBackground:True

IsThreadPoolThread:True

這里可以看到,WEB線程是一個(gè)沒有名稱的線程池中的線程,如果刷新這個(gè)頁(yè)面,還有機(jī)會(huì)看到 ManagedThreadId 在不斷變化,并且可能重復(fù)出現(xiàn)。說明WEB程序有機(jī)會(huì)運(yùn)行于線程池中的不同線程。

為了模擬多用戶并發(fā)訪問的情況,我們需要對(duì)這個(gè)處理程序添加人為的延時(shí),并輸出線程相關(guān)信息與開始結(jié)束時(shí)間,再通過客戶端程序同時(shí)發(fā)起多個(gè)請(qǐng)求,查看返回的內(nèi)容,分析請(qǐng)求的處理情況。

  1. public void ProcessRequest(HttpContext context)  
  2. {  
  3.     DateTime begin = DateTime.Now;  
  4.     int t1, t2, t3;  
  5.     ThreadPool.GetAvailableThreads(out t1, out t3);  
  6.     ThreadPool.GetMaxThreads(out t2, out t3);  
  7.     Thread.Sleep(TimeSpan.FromSeconds(10));  
  8.     DateTime end = DateTime.Now;  
  9.     context.Response.ContentType = "text/plain";  
  10.     var thread = Thread.CurrentThread;  
  11.     context.Response.Write(  
  12.         string.Format("TId:{0}\tApp:{1}\tBegin:{2:mm:ss,ffff}\tEnd:{3:mm:ss,ffff}\tTPool:{4}",   
  13.             thread.ManagedThreadId,  
  14.             context.ApplicationInstance.GetHashCode(),  
  15.             begin,  
  16.             end,  
  17.             t2 - t1  
  18.             )  
  19.         );  

我們用一個(gè)命令行程序來(lái)發(fā)起請(qǐng)求,并顯示結(jié)果。

  1. static void Main()  
  2. {  
  3.     var url = new Uri("http://localhost:8012/Handler.ashx");  
  4.     var num = 50;  
  5.     for (int i = 0; i < num; i++)  
  6.     {  
  7.         var request = WebRequest.Create(url);  
  8.         request.GetResponseAsync().ContinueWith(t =>  
  9.         {  
  10.             var stream = t.Result.GetResponseStream();  
  11.             using (TextReader tr = new StreamReader(stream))  
  12.             {  
  13.                 Console.WriteLine(tr.ReadToEnd());  
  14.             }  
  15.         });  
  16.     }  
  17.     Console.ReadLine();  

這里,我們同時(shí)發(fā)起了50個(gè)請(qǐng)求,然后觀察響應(yīng)的情況。

【注意】后面的結(jié)果會(huì)因?yàn)椴僮飨到y(tǒng)、IIS版本、管道模式、.NET版本、配置項(xiàng) 的不同而不同,以下結(jié)果為在Windows Server 2008 R2 + IIS7.5 + .NET 4.5 beta(.NET 4 runtime) + 默認(rèn)配置 中測(cè)試的結(jié)果,在沒有特別說明的情況下,均為重啟IIS后第一次運(yùn)行的情況。
這個(gè)程序在我的電腦運(yùn)行結(jié)果是這樣的: 

  1. TId:6   App:35898671    Begin:55:30,3176        End:55:40,3182  TPool:2  
  2. TId:5   App:22288629    Begin:55:30,3176        End:55:40,3212  TPool:2  
  3. TId:7   App:12549444    Begin:55:31,0426        End:55:41,0432  TPool:3  
  4. TId:8   App:22008501    Begin:55:31,5747        End:55:41,5752  TPool:4  
  5. TId:9   App:37121646    Begin:55:32,1067        End:55:42,1073  TPool:5  
  6. TId:10  App:33156464    Begin:55:32,6387        End:55:42,6393  TPool:6  
  7. TId:11  App:7995840     Begin:55:33,1707        End:55:43,1713  TPool:7  
  8. TId:12  App:36610825    Begin:55:33,7028        End:55:43,7033  TPool:8  
  9. TId:13  App:20554616    Begin:55:34,2048        End:55:44,2054  TPool:9  
  10. TId:14  App:15510466    Begin:55:35,2069        End:55:45,2074  TPool:10  
  11. TId:15  App:23324256    Begin:55:36,2049        End:55:46,2055  TPool:11  
  12. TId:16  App:34250480    Begin:55:37,2050        End:55:47,2055  TPool:12  
  13. TId:17  App:58408916    Begin:55:38,2050        End:55:48,2056  TPool:13  
  14. TId:18  App:2348279     Begin:55:39,2051        End:55:49,2057  TPool:14  
  15. TId:19  App:61669314    Begin:55:40,2051        End:55:50,2057  TPool:15  
  16. TId:6   App:35898671    Begin:55:40,3212        End:55:50,3217  TPool:15  
  17. TId:5   App:22288629    Begin:55:40,3232        End:55:50,3237  TPool:15  
  18. TId:7   App:12549444    Begin:55:41,0432        End:55:51,0438  TPool:15  
  19. TId:8   App:22008501    Begin:55:41,5752        End:55:51,5758  TPool:15  
  20. TId:9   App:37121646    Begin:55:42,1073        End:55:52,1078  TPool:15  
  21. TId:10  App:33156464    Begin:55:42,6393        End:55:52,6399  TPool:15  
  22. TId:11  App:7995840     Begin:55:43,1713        End:55:53,1719  TPool:15  
  23. TId:12  App:36610825    Begin:55:43,7043        End:55:53,7049  TPool:15  
  24. TId:13  App:20554616    Begin:55:44,2054        End:55:54,2059  TPool:15  
  25. TId:20  App:36865354    Begin:55:45,2074        End:55:55,2080  TPool:16  
  26. TId:14  App:15510466    Begin:55:45,2084        End:55:55,2090  TPool:16  
  27. TId:21  App:3196068     Begin:55:46,2055        End:55:56,2061  TPool:17  
  28. TId:15  App:23324256    Begin:55:46,2065        End:55:56,2071  TPool:17  
  29. TId:22  App:4186222     Begin:55:47,2055        End:55:57,2061  TPool:18  
  30. TId:16  App:34250480    Begin:55:47,2065        End:55:57,2071  TPool:18  
  31. TId:23  App:764807      Begin:55:48,2046        End:55:58,2052  TPool:19  
  32. TId:17  App:58408916    Begin:55:48,2056        End:55:58,2062  TPool:19  
  33. TId:24  App:10479095    Begin:55:49,2047        End:55:59,2052  TPool:20  
  34. TId:18  App:2348279     Begin:55:49,2057        End:55:59,2062  TPool:20  
  35. TId:25  App:4684807     Begin:55:50,2047        End:56:00,2053  TPool:21  
  36. TId:19  App:61669314    Begin:55:50,2057        End:56:00,2063  TPool:21  
  37. TId:6   App:35898671    Begin:55:50,3227        End:56:00,3233  TPool:21  
  38. TId:5   App:22288629    Begin:55:50,3237        End:56:00,3243  TPool:21  
  39. TId:7   App:12549444    Begin:55:51,0438        End:56:01,0443  TPool:21  
  40. TId:8   App:22008501    Begin:55:51,5758        End:56:01,5764  TPool:21  
  41. TId:9   App:37121646    Begin:55:52,1078        End:56:02,1084  TPool:21  
  42. TId:10  App:33156464    Begin:55:52,6399        End:56:02,6404  TPool:21  
  43. TId:11  App:7995840     Begin:55:53,1719        End:56:03,1725  TPool:21  
  44. TId:26  App:41662089    Begin:55:53,7049        End:56:03,7055  TPool:22  
  45. TId:12  App:36610825    Begin:55:53,7059        End:56:03,7065  TPool:22  
  46. TId:13  App:20554616    Begin:55:54,2069        End:56:04,2075  TPool:22  
  47. TId:27  App:46338128    Begin:55:55,2070        End:56:05,2076  TPool:23  
  48. TId:14  App:15510466    Begin:55:55,2090        End:56:05,2096  TPool:23  
  49. TId:20  App:36865354    Begin:55:55,2090        End:56:05,2096  TPool:23  
  50. TId:28  App:28975576    Begin:55:56,2051        End:56:06,2056  TPool:24 

從這個(gè)結(jié)果大概可以看出,開始兩個(gè)請(qǐng)求幾乎同時(shí)開始處理,因?yàn)榫€程池最小線程數(shù)為2(可配置),緊接著后面的請(qǐng)求會(huì)每隔半秒鐘開始一個(gè),因?yàn)槿绻刂械木€程都忙,會(huì)等待半秒(.NET版本不同而不同),如果還是沒有線程釋放則開啟新的線程,直到達(dá)到最大線程數(shù)(可配置)。未能在線程池中處理的請(qǐng)求將被放入請(qǐng)求隊(duì)列,當(dāng)一個(gè)線程釋放后,下一個(gè)請(qǐng)求緊接著開始在該線程處理。

最終50個(gè)請(qǐng)求共產(chǎn)生24個(gè)線程,總用時(shí)約35.9秒。

光看數(shù)據(jù)不夠形象,用簡(jiǎn)單的代碼把數(shù)據(jù)轉(zhuǎn)換成圖形吧,下面是100個(gè)請(qǐng)求的處理過程。

我們可以看到,當(dāng)WEB線程長(zhǎng)時(shí)間被占用時(shí),請(qǐng)求會(huì)由于線程池而阻塞,同時(shí)產(chǎn)生大量的線程,最終響應(yīng)時(shí)間變長(zhǎng)。

作為對(duì)比,我們列出處理時(shí)間10毫秒的數(shù)據(jù)。

  1. TId:6   App:44665200    Begin:41:07,9932        End:41:08,0032  TPool:2  
  2. TId:5   App:37489757    Begin:41:07,9932        End:41:08,0032  TPool:2  
  3. TId:5   App:44665200    Begin:41:08,0042        End:41:08,0142  TPool:2  
  4. TId:6   App:37489757    Begin:41:08,0052        End:41:08,0152  TPool:2  
  5. TId:5   App:44665200    Begin:41:08,0142        End:41:08,0242  TPool:2  
  6. TId:6   App:37489757    Begin:41:08,0152        End:41:08,0252  TPool:2  
  7. TId:5   App:44665200    Begin:41:08,0242        End:41:08,0342  TPool:2  
  8. TId:6   App:37489757    Begin:41:08,0252        End:41:08,0352  TPool:2  
  9. TId:5   App:44665200    Begin:41:08,0342        End:41:08,0442  TPool:2  
  10. TId:6   App:37489757    Begin:41:08,0352        End:41:08,0452  TPool:2  
  11. TId:5   App:44665200    Begin:41:08,0442        End:41:08,0542  TPool:2  
  12. TId:6   App:37489757    Begin:41:08,0452        End:41:08,0552  TPool:2  
  13. TId:5   App:44665200    Begin:41:08,0542        End:41:08,0642  TPool:2  
  14. TId:6   App:37489757    Begin:41:08,0552        End:41:08,0652  TPool:2  
  15. TId:5   App:44665200    Begin:41:08,0642        End:41:08,0742  TPool:2  
  16. TId:6   App:37489757    Begin:41:08,0652        End:41:08,0752  TPool:2  
  17. TId:5   App:44665200    Begin:41:08,0742        End:41:08,0842  TPool:2  
  18. TId:6   App:37489757    Begin:41:08,0752        End:41:08,0852  TPool:2  
  19. TId:5   App:44665200    Begin:41:08,0842        End:41:08,0942  TPool:2  
  20. TId:6   App:37489757    Begin:41:08,0852        End:41:08,0952  TPool:2  
  21. TId:5   App:44665200    Begin:41:08,0942        End:41:08,1042  TPool:2  
  22. TId:6   App:37489757    Begin:41:08,0952        End:41:08,1052  TPool:2  
  23. TId:5   App:44665200    Begin:41:08,1042        End:41:08,1142  TPool:2  
  24. TId:6   App:37489757    Begin:41:08,1052        End:41:08,1152  TPool:2  
  25. TId:5   App:44665200    Begin:41:08,1142        End:41:08,1242  TPool:2  
  26. TId:6   App:37489757    Begin:41:08,1152        End:41:08,1252  TPool:2  
  27. TId:5   App:44665200    Begin:41:08,1242        End:41:08,1342  TPool:2  
  28. TId:6   App:37489757    Begin:41:08,1252        End:41:08,1352  TPool:2  
  29. TId:5   App:44665200    Begin:41:08,1342        End:41:08,1442  TPool:2  
  30. TId:6   App:37489757    Begin:41:08,1352        End:41:08,1452  TPool:2  
  31. TId:5   App:44665200    Begin:41:08,1442        End:41:08,1542  TPool:2  
  32. TId:6   App:37489757    Begin:41:08,1452        End:41:08,1552  TPool:2  
  33. TId:5   App:44665200    Begin:41:08,1542        End:41:08,1642  TPool:2  
  34. TId:6   App:37489757    Begin:41:08,1552        End:41:08,1652  TPool:2  
  35. TId:5   App:44665200    Begin:41:08,1642        End:41:08,1742  TPool:2  
  36. TId:6   App:37489757    Begin:41:08,1652        End:41:08,1752  TPool:2  
  37. TId:5   App:44665200    Begin:41:08,1742        End:41:08,1842  TPool:3  
  38. TId:7   App:12547953    Begin:41:08,1752        End:41:08,1852  TPool:3  
  39. TId:6   App:37489757    Begin:41:08,1762        End:41:08,1862  TPool:3  
  40. TId:5   App:44665200    Begin:41:08,1842        End:41:08,1942  TPool:3  
  41. TId:7   App:12547953    Begin:41:08,1852        End:41:08,1952  TPool:3  
  42. TId:6   App:37489757    Begin:41:08,1862        End:41:08,1962  TPool:3  
  43. TId:5   App:44665200    Begin:41:08,1942        End:41:08,2042  TPool:3  
  44. TId:7   App:12547953    Begin:41:08,1952        End:41:08,2092  TPool:3  
  45. TId:6   App:37489757    Begin:41:08,1962        End:41:08,2102  TPool:3  
  46. TId:5   App:44665200    Begin:41:08,2052        End:41:08,2152  TPool:3  
  47. TId:7   App:12547953    Begin:41:08,2092        End:41:08,2192  TPool:3  
  48. TId:6   App:37489757    Begin:41:08,2102        End:41:08,2202  TPool:3  
  49. TId:5   App:44665200    Begin:41:08,2152        End:41:08,2252  TPool:3  
  50. TId:7   App:12547953    Begin:41:08,2192        End:41:08,2292  TPool:3 

共產(chǎn)生線程3個(gè),總用時(shí)0.236秒。

根據(jù)以上的數(shù)據(jù),我們可以得出結(jié)論,要提高系統(tǒng)響應(yīng)時(shí)間與并發(fā)處理數(shù),應(yīng)盡可能減少WEB線程的等待。

【略】請(qǐng)各位自行查驗(yàn)當(dāng)一次并發(fā)全部處理完畢后再次測(cè)試的處理情況。

【略】請(qǐng)各位自行查驗(yàn)當(dāng)處理程序中使用線程池處理等待任務(wù)的處理情況。

如何減少WEB線程的等待呢,那就應(yīng)該盡早的結(jié)果ProcessRequest方法,前一篇中講到,對(duì)于一些需要等待完成的任務(wù),可以使用異步方法來(lái)做,于是我們可以在ProcessRequest中調(diào)用異步方法,但問題是當(dāng)ProcessRequest結(jié)束后,請(qǐng)求處理也即將結(jié)束,一但請(qǐng)求結(jié)束,將沒有辦法在這一次請(qǐng)求中返回結(jié)果給客戶端,但是此時(shí),異步任務(wù)還沒有完成,當(dāng)異步任務(wù)完成時(shí),也許再也沒有辦法將結(jié)果傳給客戶端了。(難道用輪詢?囧)

 我們需要的方案是,處理請(qǐng)求時(shí)可以暫停處理(不是暫停線程),并保持客戶端連接,在需要時(shí),向客戶端輸出結(jié)果,并結(jié)束請(qǐng)求。

在這個(gè)模型中,可以看到,對(duì)于WebServerRuntime來(lái)說,我們的請(qǐng)求處理程序就是一個(gè)異步方法,而對(duì)于客戶端來(lái)說,卻并不知道后面的處理情況。無(wú)論在WebServerRuntime或是我們的處理程序,都沒有直接占用線程,一切由何時(shí)SetComplete決定。同時(shí)可以看到,這種模式需要WebServerRuntime的緊密配合,提供調(diào)用異步方法的接口。在ASP.NET中,這個(gè)接口就是IHttpAsyncHandler。

異步ASP.NET處理程序

首先,我們來(lái)實(shí)現(xiàn)第一個(gè)異步處理程序,在適當(dāng)?shù)臅r(shí)候觸發(fā)結(jié)束,在開始和結(jié)束時(shí)輸出一些信息。 

  1. public class Handler : IHttpHandler, IHttpAsyncHandler  
  2. {  
  3.     public void ProcessRequest(HttpContext context)  
  4.     {  
  5.         //異步處理器不執(zhí)行該方法  
  6.     }  
  7.  
  8.     public bool IsReusable  
  9.     {  
  10.         //設(shè)置允許重用對(duì)象  
  11.         get { return false; }  
  12.     }  
  13.       
  14.     //請(qǐng)求開始時(shí)由ASP.NET調(diào)用此方法  
  15.     public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)  
  16.     {  
  17.         context.Response.ContentType = "text/xml";  
  18.         context.Response.Write("App:");  
  19.         context.Response.Write(context.ApplicationInstance.GetHashCode());  
  20.         context.Response.Write("\tBegin:");  
  21.         context.Response.Write(DateTime.Now.ToString("mm:ss,ffff"));  
  22.         //輸出當(dāng)前線程  
  23.         context.Response.Write("\tThreadId:");  
  24.         context.Response.Write(Thread.CurrentThread.ManagedThreadId);  
  25.         //構(gòu)建異步結(jié)果并返回  
  26.         var result = new WebAsyncResult(cb, context);  
  27.         //用一個(gè)定時(shí)器來(lái)模擬異步觸發(fā)完成  
  28.         Timer timer = null;  
  29.         timer = new Timer(o =>  
  30.         {  
  31.             result.SetComplete();  
  32.             timer.Dispose();  
  33.         }, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));  
  34.         return result;  
  35.     }  
  36.  
  37.     //異步結(jié)束時(shí),由ASP.NET調(diào)用此方法  
  38.     public void EndProcessRequest(IAsyncResult result)  
  39.     {  
  40.         WebAsyncResult webresult = (WebAsyncResult)result;  
  41.         webresult.Context.Response.Write("\tEnd:");  
  42.         webresult.Context.Response.Write(DateTime.Now.ToString("mm:ss,ffff"));  
  43.         //輸出當(dāng)前線程  
  44.         webresult.Context.Response.Write("\tThreadId:");  
  45.         webresult.Context.Response.Write(Thread.CurrentThread.ManagedThreadId);  
  46.     }  
  47.  
  48.     //WEB異步方法結(jié)果  
  49.     class WebAsyncResult : IAsyncResult  
  50.     {  
  51.         private AsyncCallback _callback;  
  52.  
  53.         public WebAsyncResult(AsyncCallback cb, HttpContext context)  
  54.         {  
  55.             Context = context;  
  56.             _callback = cb;  
  57.         }  
  58.  
  59.         //當(dāng)異步完成時(shí)調(diào)用該方法  
  60.         public void SetComplete()  
  61.         {  
  62.             IsCompleted = true;  
  63.             if (_callback != null)  
  64.             {  
  65.                 _callback(this);  
  66.             }  
  67.         }  
  68.  
  69.         public HttpContext Context  
  70.         {  
  71.             get;  
  72.             private set;  
  73.         }  
  74.  
  75.         public object AsyncState  
  76.         {  
  77.             get { return null; }  
  78.         }  
  79.  
  80.         //由于ASP.NET不會(huì)等待WEB異步方法,所以不使用此對(duì)象  
  81.         public WaitHandle AsyncWaitHandle  
  82.         {  
  83.             get { throw new NotImplementedException(); }  
  84.         }  
  85.  
  86.         public bool CompletedSynchronously  
  87.         {  
  88.             get { return false; }  
  89.         }  
  90.  
  91.         public bool IsCompleted  
  92.         {  
  93.             get;  
  94.             private set;  
  95.         }  
  96.     }  

在這里,我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的AsyncResult,由于ASP.NET通過回調(diào)方法獲取異步完成,不會(huì)等待異步,所以不需要WaitHandle。在開始請(qǐng)求時(shí),建立一個(gè)AsyncResult后直接返回,當(dāng)異步完成時(shí),調(diào)用AsyncResult的SetComplete方法,調(diào)用回調(diào)方法,再由 ASP.NET調(diào)用異步結(jié)束。此時(shí)整個(gè)請(qǐng)求即完成。

當(dāng)我們?cè)L問這個(gè)地址,可以得到類似于下面的結(jié)果:

App:11240144 Begin:37:24,2676 ThreadId:6 End:37:29,2619 ThreadId:6

可以看到開始和結(jié)束在同一個(gè)線程中運(yùn)行。 

當(dāng)有多個(gè)并發(fā)請(qǐng)求時(shí),線程池將忙碌起來(lái),開始與結(jié)束處理也獎(jiǎng)有機(jī)會(huì)運(yùn)行于不同的線程上。50個(gè)請(qǐng)求并發(fā)時(shí)的處理數(shù)據(jù):

  1. App:52307948    Begin:39:47,8128        ThreadId:6      End:39:52,8231  ThreadId:5  
  2. App:58766839    Begin:39:47,8358        ThreadId:5      End:39:52,8321  ThreadId:7  
  3. App:23825510    Begin:39:47,8348        ThreadId:5      End:39:52,8321  ThreadId:7  
  4. App:30480920    Begin:39:47,8348        ThreadId:5      End:39:52,8321  ThreadId:7  
  5. App:62301924    Begin:39:47,8348        ThreadId:6      End:39:52,8321  ThreadId:6  
  6. App:28062782    Begin:39:47,8338        ThreadId:5      End:39:52,8321  ThreadId:6  
  7. App:41488021    Begin:39:47,8338        ThreadId:6      End:39:52,8321  ThreadId:7  
  8. App:15315213    Begin:39:47,8338        ThreadId:6      End:39:52,8321  ThreadId:6  
  9. App:17228638    Begin:39:47,8328        ThreadId:5      End:39:52,8321  ThreadId:7  
  10. App:51438283    Begin:39:47,8328        ThreadId:6      End:39:52,8321  ThreadId:6  
  11. App:32901400    Begin:39:47,8328        ThreadId:5      End:39:52,8321  ThreadId:7  
  12. App:61925337    Begin:39:47,8358        ThreadId:6      End:39:52,8321  ThreadId:6  
  13. App:24914721    Begin:39:47,8318        ThreadId:6      End:39:52,8321  ThreadId:6  
  14. App:26314214    Begin:39:47,8318        ThreadId:6      End:39:52,8321  ThreadId:6  
  15. App:51004322    Begin:39:47,8358        ThreadId:6      End:39:52,8321  ThreadId:6  
  16. App:51484875    Begin:39:47,8308        ThreadId:5      End:39:52,8321  ThreadId:7  
  17. App:19420176    Begin:39:47,8308        ThreadId:6      End:39:52,8321  ThreadId:6  
  18. App:16868352    Begin:39:47,8298        ThreadId:6      End:39:52,8321  ThreadId:7  
  19. App:61115195    Begin:39:47,8298        ThreadId:5      End:39:52,8321  ThreadId:6  
  20. App:63062333    Begin:39:47,8288        ThreadId:6      End:39:52,8321  ThreadId:6  
  21. App:53447344    Begin:39:47,8298        ThreadId:5      End:39:52,8321  ThreadId:7  
  22. App:31665793    Begin:39:47,8288        ThreadId:5      End:39:52,8321  ThreadId:7  
  23. App:2174563     Begin:39:47,8288        ThreadId:6      End:39:52,8321  ThreadId:6  
  24. App:12053474    Begin:39:47,8318        ThreadId:5      End:39:52,8321  ThreadId:7  
  25. App:41728762    Begin:39:47,8278        ThreadId:6      End:39:52,8321  ThreadId:6  
  26. App:6385742     Begin:39:47,8278        ThreadId:5      End:39:52,8321  ThreadId:7  
  27. App:13009416    Begin:39:47,8268        ThreadId:6      End:39:52,8321  ThreadId:6  
  28. App:43205102    Begin:39:47,8268        ThreadId:5      End:39:52,8321  ThreadId:7  
  29. App:14333193    Begin:39:47,8268        ThreadId:6      End:39:52,8321  ThreadId:6  
  30. App:2808346     Begin:39:47,8258        ThreadId:6      End:39:52,8321  ThreadId:6  
  31. App:37489757    Begin:39:47,8128        ThreadId:5      End:39:52,8231  ThreadId:6  
  32. App:34106743    Begin:39:47,8258        ThreadId:5      End:39:52,8321  ThreadId:7  
  33. App:30180123    Begin:39:47,8248        ThreadId:6      End:39:52,8321  ThreadId:6  
  34. App:44313942    Begin:39:47,8248        ThreadId:5      End:39:52,8321  ThreadId:7  
  35. App:12611187    Begin:39:47,8248        ThreadId:6      End:39:52,8321  ThreadId:6  
  36. App:7141266     Begin:39:47,8238        ThreadId:5      End:39:52,8321  ThreadId:7  
  37. App:25425822    Begin:39:47,8278        ThreadId:5      End:39:52,8321  ThreadId:7  
  38. App:51288387    Begin:39:47,8238        ThreadId:5      End:39:52,8321  ThreadId:7  
  39. App:66166301    Begin:39:47,8228        ThreadId:6      End:39:52,8321  ThreadId:6  
  40. App:34678979    Begin:39:47,8228        ThreadId:6      End:39:52,8321  ThreadId:7  
  41. App:10104599    Begin:39:47,8218        ThreadId:5      End:39:52,8321  ThreadId:6  
  42. App:47362231    Begin:39:47,8258        ThreadId:5      End:39:52,8321  ThreadId:7  
  43. App:40535505    Begin:39:47,8218        ThreadId:6      End:39:52,8321  ThreadId:7  
  44. App:20726372    Begin:39:47,8368        ThreadId:5      End:39:52,8321  ThreadId:5  
  45. App:2730334     Begin:39:47,8368        ThreadId:6      End:39:52,8321  ThreadId:6  
  46. App:59884855    Begin:39:47,8368        ThreadId:5      End:39:52,8321  ThreadId:7  
  47. App:39774547    Begin:39:47,8238        ThreadId:6      End:39:52,8321  ThreadId:6  
  48. App:12070837    Begin:39:47,8378        ThreadId:6      End:39:52,8491  ThreadId:7  
  49. App:64828693    Begin:39:47,8218        ThreadId:5      End:39:52,8331  ThreadId:6  
  50. App:14509978    Begin:39:47,9308        ThreadId:6      End:39:52,9281  ThreadId:5 

可以看到,從始至終只由3個(gè)線程處理所有的請(qǐng)求,總共時(shí)間約5.12秒。

為簡(jiǎn)化分析,我們用下面的圖來(lái)示意異步處理程序的并發(fā)處理過程。

這樣,我們就可以通過異步的方式,將WEB線程撤底釋放出來(lái)。由WEB線程進(jìn)行請(qǐng)求的接收與結(jié)束處理,耗時(shí)的操作與等待都進(jìn)行異步處理。這樣少量的WEB線程就可以承受大量的并發(fā)請(qǐng)求,WEB線程將不再成為系統(tǒng)的瓶頸。

在大并發(fā)的異步模式下,和前面的數(shù)據(jù)相比較,可以看到HttpApplication的對(duì)象數(shù)量隨并發(fā)處理數(shù)提高而提高,隨之帶來(lái)的一系列數(shù)據(jù)結(jié)構(gòu),如 HttpHandler緩存,是需要考慮的內(nèi)存開銷。同時(shí),在異步模式下,請(qǐng)求的完成需要編程的方式來(lái)控制,在觸發(fā)完成前,客戶端連接、 HttpContext對(duì)象都保持活動(dòng)狀態(tài),客戶端也一直保持等待,直到超時(shí)。因此,異步模式下需要更細(xì)致的資源操作。

我們來(lái)看ASP.NET異步 的典型應(yīng)用場(chǎng)景。

場(chǎng)景一:處理過程中有需要等待的任務(wù),并且可以使用異步完成的。

  1. //同步方法  
  2. public void ProcessRequest(HttpContext context)  
  3. {  
  4.     FileStream fs = new FileStream("", FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, FileOptions.Asynchronous);  
  5.     fs.CopyTo(context.Response.OutputStream);  
  6. }  
  7.       
  8. //異步方法開始  
  9. public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)  
  10. {  
  11.     FileStream fs = new FileStream("D:\\a.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, FileOptions.Asynchronous);  
  12.     var task = fs.CopyToAsync(context.Response.OutputStream);  
  13.     task.GetAwaiter().OnCompleted(() => cb(task));  
  14.     return task;  
  15. }  
  16.  
  17. //異步方法結(jié)束  
  18. public void EndProcessRequest(IAsyncResult result)  
  19. {  

這個(gè)處理程序讀取服務(wù)器的文件并輸出到客戶端。

  1. //同步方法  
  2. public void ProcessRequest(HttpContext context)  
  3. {  
  4.     var url = context.Request.QueryString["url"];  
  5.     var request = (HttpWebRequest)WebRequest.Create(url);  
  6.     var response = request.GetResponse();  
  7.     var stream = response.GetResponseStream();  
  8.     stream.CopyTo(context.Response.OutputStream);  
  9. }  
  10.       
  11. //異步方法開始  
  12. public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)  
  13. {  
  14.     //構(gòu)建異步結(jié)果并返回  
  15.     var result = new WebAsyncResult(cb, context);  
  16.  
  17.     var url = context.Request.QueryString["url"];  
  18.     var request = (HttpWebRequest)WebRequest.Create(url);  
  19.     var responseTask = request.GetResponseAsync();  
  20.     responseTask.GetAwaiter().OnCompleted(() =>  
  21.     {  
  22.         var stream = responseTask.Result.GetResponseStream();  
  23.         stream.CopyToAsync(context.Response.OutputStream).GetAwaiter().OnCompleted(() =>  
  24.         {  
  25.             result.SetComplete();  
  26.         });  
  27.     });  
  28.  
  29.     return result;  
  30. }  
  31.  
  32. //異步方法結(jié)束  
  33. public void EndProcessRequest(IAsyncResult result)  
  34. {  

這是一個(gè)簡(jiǎn)單的代理,服務(wù)器獲取WEB資源后寫回。

在這類程序中,我們提供的異步處理程序調(diào)用了IOCP異步方法,使得大量節(jié)省了WEB線程的占用,相比同步處理程序來(lái)說,并發(fā)量會(huì)得到相當(dāng)大的提升。

【注意】前面提到,由于WEB線程屬于線程池線程,因此,如果在線程池中加入任務(wù),將同樣會(huì)影響并發(fā)處理數(shù)。而在異步處理程序中,由線程池來(lái)完成異步將得不到任何本質(zhì)上的提升,因此在異步處理程序中禁止操作線程池(ThreadPool.QueueUserWorkItem、 delegate.BeginInvoke,Task.Run等)。如果確定需要使用多線程來(lái)處理大量的計(jì)算,需要自己開啟線程或?qū)崿F(xiàn)自己的線程池。

  1. public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)  
  2. {  
  3.     return new Action(() =>  
  4.     {  
  5.         Thread.Sleep(1000);  
  6.         context.Response.Write("OK");  
  7.     }).BeginInvoke(cb, extraData);  

上面的代碼將無(wú)法達(dá)到異步的效果。

雖然等待工作交由另一線程去操作,但是該線程與WEB線程性質(zhì)相同,同樣會(huì)導(dǎo)致其他請(qǐng)求阻塞。

【思考】如果我們的程序中的確需要有大量的計(jì)算,那么可以考慮將這些計(jì)算提取到獨(dú)立的應(yīng)用服務(wù)器中,然后通過網(wǎng)絡(luò)IOCP異步調(diào)用,達(dá)到WEB服務(wù)器的高吞吐量與系統(tǒng)的平行擴(kuò)展性。

典型應(yīng)用場(chǎng)景二:長(zhǎng)連接消息推送。

一般來(lái)說,在WEB中獲取服務(wù)器消息,采用輪詢的方式,這種方式不可避免會(huì)有延時(shí),當(dāng)我們需要即時(shí)消息的推送時(shí)(如WEBIM),需要用到長(zhǎng)連接。

長(zhǎng)連接方式,由客戶端發(fā)起請(qǐng)求,服務(wù)器端接收后暫停處理并保持連接,當(dāng)需要發(fā)送消息給客戶端時(shí),輸出內(nèi)容并結(jié)束處理,客戶端得到消息或者超時(shí)后,再次發(fā)起連接。如此達(dá)到在HTTP協(xié)議上服務(wù)器消息即時(shí)推送到客戶端的目的。

在這種情況下,我們希望服務(wù)器盡可能長(zhǎng)時(shí)間保持連接,如果采用同步處理程序,則連接數(shù)受到服務(wù)器線程數(shù)的限制,而異步處理程序則可以很好的解決這個(gè)問題。異步處理程序開始時(shí),收集相關(guān)信息,并放入集合后返回異步結(jié)果。當(dāng)需要向這個(gè)客戶端發(fā)送消息時(shí),從客戶端集合中找到需要發(fā)送的目標(biāo),發(fā)送完成即可。

首先,我們需要對(duì)客戶端進(jìn)行標(biāo)識(shí),這個(gè)標(biāo)識(shí)往往采用sessionid來(lái)做,本例中簡(jiǎn)單起見,通過客戶端傳遞參數(shù)獲取。

  1. public class WebAsyncResult : IAsyncResult  
  2. {  
  3.     private AsyncCallback _callback;  
  4.  
  5.     public WebAsyncResult(AsyncCallback cb, HttpContext context, string clientID)  
  6.     {  
  7.         Context = context;  
  8.         ClientID = clientID;  
  9.         _callback = cb;  
  10.     }  
  11.  
  12.     //當(dāng)異步完成時(shí)調(diào)用該方法  
  13.     public void SetComplete()  
  14.     {  
  15.         IsCompleted = true;  
  16.         if (_callback != null)  
  17.         {  
  18.             _callback(this);  
  19.         }  
  20.     } 

我們需要一個(gè)集合來(lái)保存連接中的客戶端,提供一個(gè)向這些客戶端發(fā)送消息的方法。

  1. public class WebAsyncResultCollection : List<WebAsyncResult>, ICollection<WebAsyncResult>  
  2. {  
  3.     private static WebAsyncResultCollection _instance = new WebAsyncResultCollection();  
  4.  
  5.     public static WebAsyncResultCollection Instance  
  6.     {  
  7.         get { return WebAsyncResultCollection._instance; }  
  8.     }  
  9.  
  10.     public bool SendMessage(string clientID, string message)  
  11.     {  
  12.         var result = this.FirstOrDefault(r => r.ClientID == clientID);  
  13.         if (result != null)  
  14.         {  
  15.             Remove(result);  
  16.             bool sendsuccess = false;  
  17.             if (result.Context.Response.IsClientConnected)  
  18.             {  
  19.                 sendsuccess = true;  
  20.                 result.Context.Response.Write(message);  
  21.             }  
  22.             result.SetComplete();  
  23.             return sendsuccess;  
  24.         }  
  25.         return false;  
  26.     }  

對(duì)于異步處理程序的開始方法,我們收集信息并放入集合。

  1. public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)  
  2. {  
  3.     var clientID = context.Request.QueryString["id"];  
  4.     WebAsyncResultCollection.Instance.SendMessage(clientID, "ClearClientID");  
  5.     WebAsyncResult result = new WebAsyncResult(cb, context, clientID);  
  6.     WebAsyncResultCollection.Instance.Add(result);  
  7.     return result;  

【不完善】由于客戶端收到一次消息后結(jié)束請(qǐng)求,由客戶端再次發(fā)起請(qǐng)求,中間會(huì)有部分時(shí)間間隙,在這間隙中向該客戶端發(fā)送的消息將丟失,解決方案是維護(hù)另一個(gè)用戶是否在線的表,如果用戶不在線,則處理離線消息,如果在線,并且正在連接中,則按上述處理,如果不在連接中,則緩存在服務(wù)器,當(dāng)客戶端再次連接時(shí),首先檢查緩存的消息,如果有未接消息,則獲取消息并立即返回。

發(fā)送消息的處理程序。

  1. public class SendMessage : IHttpHandler  
  2. {  
  3.  
  4.     public void ProcessRequest(HttpContext context)  
  5.     {  
  6.         var clientID = context.Request.QueryString["clientID"];  
  7.         var message = context.Request.QueryString["message"];  
  8.         WebAsyncResultCollection.Instance.SendMessage(clientID, message);  
  9.     }  
  10.  
  11.     public bool IsReusable  
  12.     {  
  13.         get 
  14.         {  
  15.             return true;  
  16.         }  
  17.     }  

可以在任何需要的位置向客戶端發(fā)送消息。

【不完善】我們需要定時(shí)刷新客戶端集合,對(duì)于長(zhǎng)時(shí)間未處理的客戶端進(jìn)行超時(shí)結(jié)束處理。

通過異步處理程序構(gòu)建的長(zhǎng)連接消息推送機(jī)制,單臺(tái)服務(wù)器可以輕松支持上萬(wàn)個(gè)并發(fā)連接。

異步Action

在ASP.NET MVC 4中,添加了對(duì)異步Action的支持。     

在ASP.NET MVC4中,整個(gè)處理過程都是異步的。

在圖中可以看到,最右邊的ActionDescriptor將決定如何調(diào)用我們的Action方法,而如何調(diào)用是由具體的Action方法形式?jīng)Q定,ASP.NET MVC會(huì)根據(jù)不同的方法形式創(chuàng)建不同的ActionDescriptor實(shí)例,從而調(diào)用不同的處理過程。對(duì)于傳統(tǒng)的方法,則使用 ReflectedActionDescriptor,他實(shí)現(xiàn)Execute方法,調(diào)用我們的Action,并在 AsyncControllerActionInvoker包裝成同步調(diào)用。而異步調(diào)用在ASP.NET MVC 4  中有兩種模式。

異步Action模式一:AsyncController/XXXAsync/XXXCompleted

我們可以使一個(gè)Controller繼承自AsyncController,按照約定同時(shí)提供兩個(gè)方法,分別命名為 XXXAsync/XXXCompleted,ASP.NET MVC則會(huì)將他們包裝成ReflectedAsyncActionDescriptor。   

  1. public class DefaultController : AsyncController  
  2. {  
  3.     public void DoAsync()  
  4.     {  
  5.         //注冊(cè)一次異步  
  6.         AsyncManager.OutstandingOperations.Increment();  
  7.         Timer timer = null;  
  8.         timer = new Timer(o =>  
  9.         {  
  10.             //一次異步完成  
  11.             AsyncManager.OutstandingOperations.Decrement();  
  12.             timer.Dispose();  
  13.         },null, 5000, 5000);  
  14.     }  
  15.  
  16.     public ActionResult DoCompleted()  
  17.     {  
  18.         return Content("OK");  
  19.     }  

 由于沒有IAsyncResult,我們需要通過AsyncManager來(lái)告訴ASP.NET MVC何時(shí)完成異步,我們可以在方法內(nèi)部在啟用異步時(shí)調(diào)用 AsyncManager.OutstandingOperations.Increment()告訴ASP.NET MVC開始了一次異步,完成異步時(shí)調(diào)用AsyncManager.OutstandingOperations.Decrement()告訴 ASP.NET MVC完成了一次異步,當(dāng)所有異步完成,AsyncManager會(huì)自動(dòng)觸發(fā)異步完成事件,調(diào)用回調(diào)方法,最終調(diào)用我們的XXXComplete方法。我們也可以用AsyncManager.Finish()也觸發(fā)所有異步完成。當(dāng)不使用任何AsyncManager時(shí),則不啟用異步。

可以看到整個(gè)異步過程由ASP.NET完成,在適當(dāng)?shù)臅r(shí)候會(huì)調(diào)用我們的方法。異步的開始、結(jié)束動(dòng)作與及如何觸發(fā)完成都在我們的代碼中體現(xiàn)。

異步Action模式二:Task Action

對(duì)于Action,如果返回的類型是 Task,ASP.NET MVC則會(huì)將他們包裝成TaskAsyncActionDescriptor。 

  1. public class DefaultController : Controller  
  2. {  
  3.     public async Task<FileResult> Download()  
  4.     {  
  5.         using (FileStream fs = new FileStream("D:\\a.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, FileOptions.Asynchronous))  
  6.         {  
  7.             byte[] data = new byte[fs.Length];  
  8.             await fs.ReadAsync(data, 0, data.Length);  
  9.             return new FileContentResult(data, "application/octet-stream");  
  10.         }  
  11.     }  

 我只需要需提供一個(gè)返回類型為Task的方法即可,我里我們采用async/await語(yǔ)法構(gòu)建一個(gè)異步方法,在方法內(nèi)部調(diào)用其他的異步方法。

相比之前的模式,簡(jiǎn)單了一些,特別是我們的Controller中,只有一個(gè)方法,異步的操作都交由Task完成。對(duì)于可以返回Task的方法來(lái)說(如通過async/await包裝多個(gè)異步方法),就顯得十分方便。

原文鏈接:http://www.cnblogs.com/wisdomqq/archive/2012/03/29/2417723.html

【編輯推薦】

  1. ASP.NET的路由系統(tǒng):URL與物理文件的分離
  2. ASP.NET MVC3 從零開始一步步構(gòu)建Web
  3. Node.js vs Opa: Web框架殺手
  4. 設(shè)計(jì)好脾氣的Web頁(yè)面
  5. Google Web App開發(fā)指南之構(gòu)建優(yōu)秀的Web Apps
責(zé)任編輯:林師授 來(lái)源: 鈞梓昊逑的博客
相關(guān)推薦

2009-07-28 16:40:11

ASP.NET異步頁(yè)面

2009-08-03 17:35:07

ASP.NET WebASP.NET編程工具

2009-12-11 14:57:56

ASP.NET

2009-08-21 17:02:20

ASP.NET異步回調(diào)

2009-12-18 16:51:41

ASP.NET概述

2009-07-22 10:13:31

異步ActionASP.NET MVC

2011-11-22 09:32:39

ASP.NET

2011-07-06 13:41:06

ASP.NET

2011-07-04 15:33:15

ASP.NET

2011-07-06 11:15:09

ASP.NET

2009-10-30 14:03:59

ASP.NET上傳文件

2009-07-22 17:45:35

ASP.NET教程

2009-03-06 10:28:30

MVCASP.NET異步Action

2009-08-03 14:22:33

什么是ASP.NET

2009-07-28 17:17:19

ASP.NET概述

2009-12-30 17:51:31

Silverlight

2015-06-29 10:00:02

ASP.NETMVC6

2009-07-27 12:22:03

ASP.NET和ASPASP.NET入門教程

2009-12-10 17:47:01

ASP.NET 2.0

2011-07-13 08:58:51

ASP.NET
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 99精品久久 | 日韩免费一区二区 | 亚洲久草| 国产农村妇女精品一区 | 欧美日韩成人一区二区 | 亚洲欧美精品 | 亚洲精品天堂 | 国产一区二区在线播放视频 | 国产1区2区在线观看 | av日日操 | 中文字幕在线观看视频网站 | 性色av一区| 黄色精品| 97在线观视频免费观看 | 日韩综合网 | 韩国成人在线视频 | 国产精品69久久久久水密桃 | 成人精品鲁一区一区二区 | 日韩精品在线观看视频 | 福利视频一区二区 | 99精品久久久久 | 黄免费观看视频 | 欧美一级欧美一级在线播放 | 亚洲品质自拍视频 | 国内精品久久久久久久影视简单 | 爱草在线 | 国产成人精品免费视频大全最热 | 中文字字幕一区二区三区四区五区 | 一区二区视频在线 | 91私密视频| 日本久久一区二区三区 | 日韩视频在线一区二区 | 精品国产精品三级精品av网址 | 在线观看国产视频 | 日韩欧美在线一区 | 视频第一区 | 亚洲精品一区二区 | 日韩欧美中文字幕在线观看 | 精品在线一区 | 伊人看片 | 久久夜色精品国产 |