(一).简单介绍实现原理
下图左为未使用异步页功能执行过程(Asp.net 1.0通常情况), 下图右为使用了异步页执行过程(Asp.net 2.0新增特性).
(Asp.net 1.0一般处理过程) (使用Asp.net 2.0新增特性异步页功能处理过程)
|
从左图中看出,在一个页面整个请求的过程中, 一个线程始终为同一个页面的请求服务.
而从右图可以看出,在一个页面请求的过程中, 可以由不同的线程为本页面请求服务.
显然,采用图中方式在客户端请求数量多时,网站整体效率较高. 因为:
1. 当未使用异步页时,一个线程只能为同一个页面的请求服务. 即使页面请求过程中处理其它的I/O等操作时,此线程也一直处于等待状态. 当此页面使用完此线程时,才将它放回到线程池. 线程数量是有限的! 所以当不使用线程时及时放回线池可以使系统性能大大提高!
2.当使用了异步页功能时,如右图中,开始Thread1是为页面服务的,但当页面处理其它的事情(比如I/O或调用其它WebService) 时,Thread1被放回线程池, 此时Thread1可以为其它页面请求服务了. 当此页面执行完自己的操作回来后, Thread2接着为页面请求服务,并不是使用的原来的线程Thread1. 这样网站的伸缩性会更好.
(二).使用方法示例
I. 用 Page.AddOnPreRenderCompleteAsync 实现异步页功能
a. Page标志加属性: Async="true", 添加后代码如下:
<%
@ Page Language
=
"
C#
"
AutoEventWireup
=
"
true
"
CodeFile
=
"
AsyncPage.aspx.cs
"
Inherits
=
"
_Default
"
Async
=
"
true
"
%>
b. 后台异步页面相关代码 :
private
WebRequest _request;
protected
void
Page_Load(
object
sender, EventArgs e)

...
{
//注册异步调用的Begin和End方法.
AddOnPreRenderCompleteAsync(
new BeginEventHandler(BeginAsyncOperation),
new EndEventHandler(EndAsyncOperation)
);
}

//
异步调用开始方法(当执行此方法时,当前线程就回到线程池,等待为其它请求服务).
IAsyncResult BeginAsyncOperation(
object
sender, EventArgs e, AsyncCallback cb,
object
state)

...
{
_request = WebRequest.Create("http://blog.csdn.net/chengking/");
return _request.BeginGetResponse(cb, state);
}

//
异步调用结束后的接收方法(异步操作执行完成后,会重新从线程池中取个线程为本页面请求服务).
void
EndAsyncOperation(IAsyncResult ar)

...
{
string text;
using (WebResponse response = _request.EndGetResponse(ar))

...{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))

...{
text = reader.ReadToEnd();
}
}
this.lbOupput.Text = text;
}
2. 数据库对象SqlCommand实现异步调用功能.
a. Page标志加属性: Async="true", 添加后代码如下:
<%
@ Page Language
=
"
C#
"
AutoEventWireup
=
"
true
"
CodeFile
=
"
AsyncPage.aspx.cs
"
Inherits
=
"
_Default
"
Async
=
"
true
"
%>
b. 后台代码
public
partial
class
AsyncVisitDatabase : System.Web.UI.Page

...
{
//定义数据操作对象
private SqlConnection _connection;
private SqlCommand _command;
private SqlDataReader _reader;

protected void Page_Load(object sender, EventArgs e)

...{
if (!IsPostBack)

...{
//注册事件Page_PreRender执行完成时执行方法
this.PreRenderComplete += new EventHandler(Page_PreRenderComplete);


/**//**//**////注册异步调用的Begin和End方法.
AddOnPreRenderCompleteAsync(
new BeginEventHandler(BeginAsyncOperation),
new EndEventHandler(EndAsyncOperation)
);
}
}

//异步调用开始方法(当执行此方法时,当前线程就回到线程池,等待为其它请求服务).
IAsyncResult BeginAsyncOperation(object sender, EventArgs e, AsyncCallback cb, object state)

...{
string connect = WebConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
_connection = new SqlConnection(connect);
_connection.Open();
_command = new SqlCommand("select * from sales", _connection);
return _command.BeginExecuteReader(cb, state);
}

//异步调用结束后的接收方法(异步操作执行完成后,会重新从线程池中取个线程为本页面请求服务).
void EndAsyncOperation(IAsyncResult ar)

...{
_reader = _command.EndExecuteReader(ar);
}

//事件Page_PreRender执行完成时执行方法,在这里可以将异步调用返回结果赋值给页面上的控件或者其它善后操作.
protected void Page_PreRenderComplete(object sender, EventArgs e)

...{
GridView1.DataSource = _reader;
GridView1.DataBind();
}

public override void Dispose()

...{
if (_connection != null)
_connection.Close();
base.Dispose();
}
}
3. 实现异步调用Webservice
a. Page标志加属性: Async="true", 添加后代码如下:
<%
@ Page Language
=
"
C#
"
AutoEventWireup
=
"
true
"
CodeFile
=
"
AsyncPage.aspx.cs
"
Inherits
=
"
_Default
"
Async
=
"
true
"
%>
b.后台代码
Webservice方法(生成数据).
public
class
WebService : System.Web.Services.WebService
...
{


public WebService () ...{

//Uncomment the following line if using designed components
//InitializeComponent();
}

[WebMethod]

public DataSet GetData() ...{
return CreateData();
}

private DataSet CreateData()

...{
DataTable dtTypeChild = new DataTable();
dtTypeChild.Columns.Add(new DataColumn("TypeID", typeof(int)));
dtTypeChild.Columns.Add(new DataColumn("TypeDetail", typeof(string)));
//Add data
DataRow drChild1 = dtTypeChild.NewRow();
drChild1["TypeID"] = 1;
drChild1["TypeDetail"] = "Apple";
dtTypeChild.Rows.Add(drChild1);
DataRow drChild2 = dtTypeChild.NewRow();
drChild2["TypeID"] = 2;
drChild2["TypeDetail"] = "orange";
dtTypeChild.Rows.Add(drChild2);
DataRow drChild3 = dtTypeChild.NewRow();
drChild3["TypeID"] = 3;
drChild3["TypeDetail"] = "banana";
dtTypeChild.Rows.Add(drChild3);
DataRow drChild4 = dtTypeChild.NewRow();
drChild4["TypeID"] = 4;
drChild4["TypeDetail"] = "pineapple";
dtTypeChild.Rows.Add(drChild4);
DataRow drChild5 = dtTypeChild.NewRow();
drChild5["TypeID"] = 5;
drChild5["TypeDetail"] = "pear";
dtTypeChild.Rows.Add(drChild5);
dtTypeChild.TableName = "fruit";

DataSet ds = new DataSet();
ds.Tables.Add(dtTypeChild);
return ds;
}
}
后台代码:
public
partial
class
AsyncVisitWebservice : System.Web.UI.Page

...
{
private King.WebService _ws;
private DataSet _ds;

protected void Page_Load(object sender, EventArgs e)

...{
if (!IsPostBack)

...{
this.PreRenderComplete += new EventHandler(Page_PreRenderComplete);
_ws = new King.WebService();
_ws.GetDataCompleted += new King.GetDataCompletedEventHandler(GetDataCompleted);
_ws.Url = new Uri(Request.Url, "WebService.asmx").ToString();
_ws.UseDefaultCredentials = true;
_ws.GetDataAsync();
}
}

void GetDataCompleted(Object source, King.GetDataCompletedEventArgs e)

...{
_ds = e.Result;
}

protected void Page_PreRenderComplete(object sender, EventArgs e)

...{
this.GridView1.DataSource = _ds;
GridView1.DataBind();
}

public override void Dispose()

...{
if (_ws != null)
_ws.Dispose();
base.Dispose();
}
}
4. 用 PageAsyncTask 实现异步调用功能(除了实现异步功能,还可以实现异步操作等待时间以及时间超时操作)
a. Page标志加属性: Async="true" AsyncTimeout="5" , 添加后代码如下:
1<%@ Page Language="C#" AutoEventWireup="true" CodeFile="AsyncPageTask.aspx.cs" Inherits="AsyncPageTask" Async="true" AsyncTimeout="5" %>
b. 后台代码
public
partial
class
AsyncPageTask : System.Web.UI.Page

...
{
private WebRequest _request;

protected void Page_Load(object sender, EventArgs e)

...{
PageAsyncTask task = new PageAsyncTask(
new BeginEventHandler(BeginAsyncOperation),
new EndEventHandler(EndAsyncOperation),
new EndEventHandler(TimeoutAsyncOperation),
null
);

RegisterAsyncTask(task);
}

IAsyncResult BeginAsyncOperation(object sender, EventArgs e, AsyncCallback cb, object state)

...{
_request = WebRequest.Create("http://blog.csdn.net/chengking");
return _request.BeginGetResponse(cb, state);
}

void EndAsyncOperation(IAsyncResult ar)

...{
string text;
using (WebResponse response = _request.EndGetResponse(ar))

...{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))

...{
text = reader.ReadToEnd();
}
}

lbDisplay.Text = text.ToString();
}

//时间超时执行操作
void TimeoutAsyncOperation(IAsyncResult ar)

...{
lbDisplay.Text = "Failture!";
}
}
第二种方式: 数据库对象SqlCommand实现异步调用功能
private
WebRequest _request;
private
SqlConnection _connection;
private
SqlCommand _command;
private
SqlDataReader _reader;
DataTable table
=
new
DataTable();



protected
void
Page_Load(
object
sender, EventArgs e)

...
{
// //注册异步调用的Begin和End方法.
if (!IsPostBack)

...{
//注册事件Page_PreRender执行完成时执行方法(其实这一个是不用写的,当你调试的时候你会发现Page_PreRenderComplete会运行了两次,因为页面打开时就已经应该运行了)
//this.PreRenderComplete += new EventHandler(Page_PreRenderComplete);


/**//**//**//**/

/**//**//**////注册异步调用的Begin和End方法.
AddOnPreRenderCompleteAsync(
new BeginEventHandler(BeginAsyncOperation), new EndEventHandler(EndAsyncOperation));
}
}


//
异步调用开始方法(当执行此方法时,当前线程就回到线程池,等待为其它请求服务).
IAsyncResult BeginAsyncOperation(
object
sender, EventArgs e, AsyncCallback cb,
object
state)

...
{
//_request = WebRequest.Create("http://blog.csdn.net/chengking/");
//return _request.BeginGetResponse(cb, state);

string connect = WebConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
_connection = new SqlConnection(connect);
_connection.Open();
_command = new SqlCommand("select * from gsjbxx", _connection);
return _command.BeginExecuteReader(cb, state);


}


//
异步调用结束后的接收方法(异步操作执行完成后,会重新从线程池中取个线程为本页面请求服务).
void
EndAsyncOperation(IAsyncResult ar)

...
{
_reader = _command.EndExecuteReader(ar);
//为了确保我得到了相应的数据并可以显示在DATAVIEW中
table.Load(_reader);

}


protected
void
Page_PreRenderComplete(
object
sender, EventArgs e)

...
{
this.GridView1.DataSource = table;
this.GridView1.DataBind();
}
对于加载时间比较长的ASP.NET页面,我们可以在客户端浏览器中显示进度条来显示页面正在装载。下面就是具体的实现过程:
1.新建项目,名字为WebPortal,在项目类型中选择Visual C#项目或者Visual Basic项目都可;
2.在模板类型中选择ASP.NET Web应用程序;
3.位置里输入:http://localhost/WebPortal;
4.添加新项:名字为ShowProgress的Web窗体。
5.在您的Web窗体ShowProgress.aspx上添加任何其他的Web服务器控件。
6.在ShowProgress.aspx上单击右键,点“查看代码”,在最上面输入:
Visual C# .NET代码
using System.Threading;
Visual Basic .NET代码
Imports System.Threading
7.在Page_Load事件里输入: Visual C# .NET代码
Response.Write("<div id='mydiv' >");
Response.Write("_");
Response.Write("</div>");
Response.Write("<script>mydiv.innerText = '';</script>");
Response.Write("<script language=javascript>;");
Response.Write("var dots = 0;var dotmax = 10;function ShowWait()");
Response.Write("{var output; output = '正在装载页面';dots++;if(dots>=dotmax)dots=1;");
Response.Write("for(var x = 0;x < dots;x++){output += '·';}mydiv.innerText = output;}");
Response.Write("function StartShowWait(){mydiv.style.visibility = 'visible'; ");
Response.Write("window.setInterval('ShowWait()',1000);}");
Response.Write("function HideWait(){mydiv.style.visibility = 'hidden';");
Response.Write("window.clearInterval();}");
Response.Write("StartShowWait();</script>");
Response.Flush();
Thread.Sleep(10000);
Visual Basic .NET代码
Response.Write("<div id='mydiv' >")
Response.Write("_")
Response.Write("</div>")
Response.Write("<script>mydiv.innerText = '';</script>")
Response.Write("<script language=javascript>;")
Response.Write("var dots = 0;var dotmax = 10;function ShowWait()")
Response.Write("{var output; output = '正在装载页面';dots++;if(dots>=dotmax)dots=1;")
Response.Write("for(var x = 0;x < dots;x++){output += '·';}mydiv.innerText = output;}")
Response.Write("function StartShowWait(){mydiv.style.visibility = 'visible'; ")
Response.Write("window.setInterval('ShowWait()',1000);}")
Response.Write("function HideWait(){mydiv.style.visibility='hidden';")
Response.Write("window.clearInterval();}")
Response.Write("StartShowWait();</script>")
Response.Flush()
Thread.Sleep(10000)
8.在ShowProgress.aspx窗体的html的中输入:
<script>
HideWait();
</script>
9.点在浏览器中查看即可