在泛型處理常式(ashx)中利用HttpTaskAsyncHandler來完成async/await之需求
今天在撰寫泛型處理常式(ashx)時,遇到一個非同步的應用上問題,也就是ProcessRequest在async/await運行尚未完成時期,它本身就運行完成,因此直接跳離,導致await Task後的回傳結果根本無處控制與顯示,因為管理者不見啦!當下很直覺的想說,那我就在ProcessRequest前面加個async就可以了啊!才發現事實不是笨蛋想的那麼簡單。
我們都知道泛型處理常式(ashx)需要繼承並實作IHttpHandler介面,問題是IHttpHandlerr介面下根本不支援ProcessRequest去處理async/await的動作,這時候就需要繼承IHttpAsyncHandler來達成非同步功能,但是引用IHttpAsyncHandler又必須要實作一堆垃圾方法,有沒有更簡單的方式呢?答案就是繼承System.Web.HttpTaskAsyncHandler類別。
繼承System.Web.HttpTaskAsyncHandler類別後,你可以繼續使用你習慣的ASHX架構繼續開發程式,然後你可以複寫(override)一個叫ProcessRequestAsync的方法,它具備非同步(async)的處理能力,更妙的是,你不用再多寫一次IsReusable屬性(Property)了,程式碼變得更精簡了!真棒!
程式碼範例如下:
<%@ WebHandler Language="C#" Class="ajaxResponse" %>
public class ajaxResponse: System.Web.HttpTaskAsyncHandler
{
//設定一個集合來存放結果集
private System.Collections.Generic.List<ORM_Class> oResult;
//泛型處理常式主要進入點
public override async System.Threading.Tasks.Task ProcessRequestAsync(System.Web.HttpContext context)
{
//指定要存取的網址,壓入非同步工作中
await PushToTask(new string[] {
"http://date.jsontest.com", //FreeJson
"http://date.jsontest.com", //FreeJson
"http://date.jsontest.com" //FreeJson
});
//輸出結果集之JSON
context.Response.ContentType = "application/json; charset=utf-8";
context.Response.Write(Newtonsoft.Json.JsonConvert.SerializeObject(oResult));
}
//非同步工作佇列處理
private async System.Threading.Tasks.Task PushToTask(System.Collections.Generic.IList<string> oURLs)
{
System.Collections.Generic.List<System.Threading.Tasks.Task> oTasks = new System.Collections.Generic.List<System.Threading.Tasks.Task>();
System.Threading.SemaphoreSlim oControl = new System.Threading.SemaphoreSlim(oURLs.Count);
oResult = new System.Collections.Generic.List<ORM_Class>();
foreach (var cURL in oURLs)
{
await oControl.WaitAsync();
oTasks.Add(System.Threading.Tasks.Task.Run(async () =>
{
try
{
using (System.Net.WebClient oWC = new System.Net.WebClient() { Encoding = System.Text.Encoding.UTF8 })
{
oResult.Add(Newtonsoft.Json.JsonConvert.DeserializeObject<ORM_Class>(
await oWC.DownloadStringTaskAsync(new System.Uri(cURL))
));
}
}
catch { return; }
finally { oControl.Release(); }
}));
}
//等候所有的工作完成
await System.Threading.Tasks.Task.WhenAll(oTasks);
}
}
//預計取用的ORM類別
class ORM_Class
{
public string time { get; set; }
public long milliseconds_since_epoch { get; set; }
public string date { get; set; }
}
輸出的結果圖如下:
相關參考:
- 非同步程式設計Async與Await
- 利用SemaphoreSlim類別來進行async/await非同步工作排程
- 再論具總量控管之.NET非同步工作排程模型
- 延伸參考:YOU'RE USING HTTPCLIENT WRONG AND IT IS DESTABILIZING YOUR SOFTWARE;HttpClient因IDisposable反而引發連線資源耗盡(TIME_WAIT)問題
- HttpClient所引爆的Sockets Port耗盡問題