在你的服務(wù)器端代碼中使用線程與創(chuàng)建異步處理(4)
發(fā)表時(shí)間:2023-08-19 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]在兩種情況下有如此的不同, 最讓人困擾的問題是響應(yīng)fast.aspx的頁面比原來要多4秒的時(shí)間,原來用非常短的時(shí)間.雖然這個(gè)例子是我們?nèi)藶? 但這個(gè)例子顯示了當(dāng)你有如此相關(guān)的響應(yīng)慢的頁面是最糟糕的情...
在兩種情況下有如此的不同, 最讓人困擾的問題是響應(yīng)fast.aspx的頁面比原來要多4秒的時(shí)間,原來用非常短的時(shí)間.雖然這個(gè)例子是我們?nèi)藶? 但這個(gè)例子顯示了當(dāng)你有如此相關(guān)的響應(yīng)慢的頁面是最糟糕的情形之一. 如果在你的應(yīng)用中慢的頁面由于行動(dòng)遲緩影響了CPU的使用, 除了增加更多的硬件設(shè)備, 好像沒有好的辦法. 然而, 應(yīng)用中的慢頁面之所以慢是因?yàn)榈却齨on-CPU-bound的操作完成, 問題不在于CPU動(dòng)力缺乏, 事實(shí)上慢的請(qǐng)求進(jìn)入到線程池中,所以其它的請(qǐng)求必須排隊(duì)直到一個(gè)線程釋放.
如果查看系統(tǒng)的擴(kuò)展性,其受到asp.net 線程池不利的影響, 所以你有很少的選擇. 你可以改變線程池的上限. 正如你看到的, 默認(rèn)的工作線程和I/O線程其上限都是25. 當(dāng)然, 提高上限是一種比較愚笨粗糙的做法. 如果你能發(fā)現(xiàn)恰好的數(shù)量來保持CPU的高利用率并且只有在服務(wù)器真的非常忙時(shí)請(qǐng)求才被延時(shí)或拒絕, 那你真是太幸運(yùn)了. 保持服務(wù)真正忙所需的線程數(shù)量并非靜止的, 要依賴于各種因素如需求和處理的復(fù)雜度而波動(dòng).
另外一個(gè)潛在的方案是慎重的確認(rèn)系統(tǒng)那些需要用時(shí)較多并且執(zhí)行non-CPU-bound請(qǐng)求, 然后分派獨(dú)特線程來響應(yīng)它們, 解除原生的線程池的線程來響應(yīng)額外的請(qǐng)求. 這就是異步處理者.
異步處理者
盡管大部分的asp.net的頁面和處理是由來自同步線程(來自進(jìn)程級(jí)的線程池)響應(yīng), 但創(chuàng)建異步請(qǐng)求的處理者是可能的.異步處理者實(shí)現(xiàn)了IHttpAsyncHandler 接口.該接口繼承于IHttpHandler:
public interface IHttpAsyncHandler : IHttpHandler
{
IAsyncResult BeginProcessRequest(HttpContext ctx,
AsyncCallback cb,
object obj);
void EndProcessRequest(IAsyncResult ar);
}
實(shí)現(xiàn)該接口必須實(shí)現(xiàn)另外來自IHttpHandler的兩個(gè)標(biāo)準(zhǔn)的方法. 第一個(gè), BeginProcessRequest, 被應(yīng)用類調(diào)用代替直接調(diào)用ProcessRequest. 它會(huì)讓處理者發(fā)起一個(gè)新的線程去處理請(qǐng)求并且會(huì)立刻從BeginProcessRequest 方法返回, 通過一個(gè)引用來驗(yàn)證IAsyncResult 實(shí)例, 目的是讓運(yùn)行時(shí)間直到操作結(jié)束. 另外一個(gè)方法是EndProcessRequest, 當(dāng)請(qǐng)求處理完成被調(diào)用, 如果有必要會(huì)被用來清除任何原來被分派的資源.
如果你做過.Net FrameWork下異步執(zhí)行的工作, 你應(yīng)該曉得最簡(jiǎn)單和最被推薦的方式使用異步委托代理執(zhí)行異步工作. 調(diào)用異步代理通過調(diào)用BeginInvoke 隱式使用來自進(jìn)程級(jí)的線程池的線程來執(zhí)行委托功能. 使用異步委托調(diào)用來實(shí)現(xiàn)異步處理者事實(shí)上非常直接, 如圖2 所示.
<!-- File: AsyncDelegate.ashx -->
<%@ WebHandler Language="C#"
Class="EssentialAspDotNet.HttpPipeline.AsyncHandler" %>
using System;
using System.Web;
using System.Threading;
using System.Diagnostics;
using System.Reflection;
namespace EssentialAspDotNet.HttpPipeline
{
public delegate void ProcessRequestDelegate(HttpContext ctx);
public class AsyncHandler : IHttpAsyncHandler
{
public void ProcessRequest(HttpContext ctx)
{
System.Threading.Thread.Sleep(2000);
ctx.Response.Output.Write(
"AsyncDelegate, threaded={0}",
AppDomain.GetCurrentThreadId());
}
public bool IsReusable
{
get { return true;}
}
public IAsyncResult BeginProcessRequest(HttpContext ctx,
AsyncCallback cb, object obj)
{
ProcessRequestDelegate prg =
new ProcessRequestDelegate(ProcessRequest);
// Fire-and-forget
return prg.BeginInvoke(ctx, cb, obj);
}
public void EndProcessRequest(IAsyncResult ar)
{
}
}
}
圖二
我實(shí)施IHttpAsyncHandler.BeginProcessRequest 調(diào)用BeginInvoke 在我的一個(gè)ProcessRequestDelegate實(shí)例上,
ProcessRequestDelegate 已經(jīng)被引用我的ProcessRequest 方法初始化. 這在一個(gè)分離的線程上執(zhí)行ProcessRequest是有效的, 從請(qǐng)求線程中分離出來.
運(yùn)行同樣的壓力測(cè)試slow.aspx頁面, 這次用我的新的asyncDelegate.ashx 處理者代替fast.aspx, 產(chǎn)生結(jié)果如下,平均響應(yīng)時(shí)間fast.aspx為4.14秒, asyncDelegate.ashx 為7.86秒, 請(qǐng)求次數(shù)為957次.
讓人失望, 對(duì)于fast.aspx來說響應(yīng)時(shí)間并沒有真正的提高. 由于異步委托調(diào)用完成不讓創(chuàng)建一個(gè)異步處理者,這是因?yàn)樗鼇碜酝瑯拥氖沁M(jìn)程級(jí)的CLR線程池(響應(yīng)請(qǐng)求). 然而一個(gè)主要的請(qǐng)求線程確實(shí)需要返回給線程池,另外一個(gè)線程被取出去執(zhí)行異步委托, 這意味著這有個(gè)用來響應(yīng)額外請(qǐng)求空線程收獲, 所以處理者沒有用. 你會(huì)得到同樣的問題, 如果你用ThreadPool.QueueUserWorkItem 去執(zhí)行異步請(qǐng)求處理, 以為它也是同樣的方式取得線程.
用定制線程異步處理者