ADO.NET2.0 异步处理的三种方式-轮循检测法
概述
一 开始一个异步过程,然后轮循检测异步过程返回的IAsyncResult对象来判断过程调用是否结束。
二 为异步过程提供一个回调函数。这种方法可以让你并行处理其他的任务。当异步过程结束时,回调函数被 触发用来处理过程结束后的清理工作以及通知程序的其他部分该异步过程已经结束。
三 第三种方法使用wait handle来处理异步过程,这是三种方法中最优雅的方法。使用这种方法你可以开启你想开启的所有异步过程,然后等待其中任何或者全部过程结束以便于做对应的处理。
轮循检测法
下面的代码用内嵌sql语句的办法取出Northwind数据库中Orders表中的前五条数据。可以通过调用BeginExecuteReader来开始一个异步过程,接下来用一个循环来等到过程结束。在等待的时候主线程会每休眠10毫秒后检测异步过程返回的结果来判断过程调用是否结束。当过程结束时用EndExecuteReader来返回结果集。
C#
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Data” %>
<%@ Import Namespace=”System.Data.SqlClient” %>
<%@ Import Namespace=”System.Configuration” %>
<script runat=”server”>
protected void Page_Load(object sender, EventArgs e)
{
SqlConnection DBCon;
SqlCommand Command = new SqlCommand();
SqlDataReader OrdersReader;
IAsyncResult ASyncResult;
DBCon = new SqlConnection();
DBCon.ConnectionString =
ConfigurationManager.ConnectionStrings[“DSN_NorthWind”].ConnectionString;
Command.CommandText =
“SELECT TOP 5 Customers.CompanyName, Customers.ContactName, “ +
“ Orders.OrderID, Orders.OrderDate, “ +
“ Orders.RequiredDate, Orders.ShippedDate “ +
“ FROM Orders, Customers “ +
“ WHERE Orders.CustomerID = Customers.CustomerID “ +
“ ORDER BY Customers.CompanyName, Customers.ContactName “
Command.CommandType = CommandType.Text;
Command.Connection = DBCon;
DBCon.Open();
// Starting the asynchronous processing
ASyncResult = Command.BeginExecuteReader();
// This loop with keep the main thread waiting until the
// asynchronous process is finished
while (!ASyncResult.IsCompleted)
{
// Sleeping current thread for 10 milliseconds
System.Threading.Thread.Sleep(10);
}
// Retrieving result from the asynchronous process
OrdersReader = Command.EndExecuteReader(ASyncResult);
// Displaying result on the screen
gvOrders.DataSource = OrdersReader;
gvOrders.DataBind();
// Closing connection
DBCon.Close();
}
</script>
如果你在while循环中打个断点,你会发现调用BeginExecuteReader方法后代码会继续执行直到异步调用结束。
Wait方法是三种异步调用方法中最为优雅的一种,这种方法提供了高度的灵活性,高效性,当然也稍微复杂一点。使用这种方法可以开始多个异步过程然后等待其中任何或者过程调用结束。使用这种方法你可以只等待那些相互依赖的过程,处理那些不和其他过程依赖的异步过程。这种方法在设计上要求考虑异步过程的细节,你必须慎重判断哪些异步过程是相互依赖的,哪些不是。这种方法的复杂性在于你必须理解其细节并且做出对应的设计。这是一种最好的使用异步调用和异步处理模型的代码设计。注意使用WaitHandle类的WaitOne方法来等待异步过程的执行结束。
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Data” %>
<%@ Import Namespace=”System.Data.SqlClient” %>
<%@ Import Namespace=”System.Configuration” %>
<script runat=”server”>
protected void Page_Load(object sender, EventArgs e)
{
SqlConnection DBCon;
SqlCommand Command = new SqlCommand();
SqlDataReader OrdersReader;
IAsyncResult ASyncResult;
System.Threading.WaitHandle WHandle;
DBCon = new SqlConnection();
DBCon.ConnectionString =
ConfigurationManager.ConnectionStrings[“DSN_NorthWind”].ConnectionString;
Command.CommandText =
“SELECT TOP 5 Customers.CompanyName, Customers.ContactName, “ +
“ Orders.OrderID, Orders.OrderDate, “ +
“ Orders.RequiredDate, Orders.ShippedDate “ +
“ FROM Orders, Customers “ +
“ WHERE Orders.CustomerID = Customers.CustomerID “ +
“ ORDER BY Customers.CompanyName, Customers.ContactName “;
Command.CommandType = CommandType.Text;
Command.Connection = DBCon;
DBCon.Open();
// Starting the asynchronous processing
ASyncResult = Command.BeginExecuteReader();
WHandle = ASyncResult.AsyncWaitHandle;
if (WHandle.WaitOne() == true)
{
// Retrieving result from the asynchronous process
OrdersReader = Command.EndExecuteReader(ASyncResult);
// Displaying result on the screen
gvOrders.DataSource = OrdersReader;
gvOrders.DataBind();
// Closing connection
DBCon.Close();
}
else
{
// Asynchronous process has timed out. Handle this
// situation here.
}
}
</script>
如果你设置断点跟踪代码会发现程序在停在调用WHandle.WaitOne方法的那行代码上。当异步调用结束后程序会继续执行。
使用多个Wait句柄
Wait方法在开始多个异步过程的时候其真正威力才能显示出来。其中一个过程查询数据库一个具体的customer的相关信息,另一个查询则处理这个customer提交的所有Order(订单)。下面的代码使用了两个Command对象,data reader对象和wait句柄。这里使用一个connection对象,由此可以看出多活动结果集(MARS)对异步处理的良好支持。
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Data” %>
<%@ Import Namespace=”System.Data.SqlClient” %>
<%@ Import Namespace=”System.Configuration” %>
<script runat=”server”>
protected void Page_Load(object sender, EventArgs e)
{
SqlConnection DBCon;
SqlCommand OrdersCommand = new SqlCommand();
SqlCommand CustCommand = new SqlCommand();
SqlDataReader OrdersReader;
SqlDataReader CustReader;
IAsyncResult OrdersASyncResult;
IAsyncResult CustAsyncResult;
System.Threading.WaitHandle[] WHandles = new
System.Threading.WaitHandle[1];
System.Threading.WaitHandle OrdersWHandle;
System.Threading.WaitHandle CustWHandle;
DBCon = new SqlConnection();
DBCon.ConnectionString =
ConfigurationManager.ConnectionStrings[“DSN_NorthWind”].ConnectionString;
CustCommand.CommandText =
“ SELECT * FROM Customers WHERE CompanyName = ‘Alfreds Futterkiste’ “;
CustCommand.CommandType = CommandType.Text;
CustCommand.Connection = DBCon;
// Selecting all orders for a specific customer
“ SELECT Customers.CompanyName, Customers.ContactName, “ +
“ Orders.OrderID, Orders.OrderDate, “ +
“ Orders.RequiredDate, Orders.ShippedDate “ +
“ FROM Orders, Customers “ +
“ WHERE Orders.CustomerID = Customers.CustomerID “ +
“ AND Customers.CompanyName = ‘Alfreds Futterkiste’ “ +
“ ORDER BY Customers.CompanyName, Customers.ContactName “;
OrdersCommand.CommandType = CommandType.Text;
OrdersCommand.Connection = DBCon;
DBCon.Open();
// Retrieving customer information asynchronously
CustAsyncResult = CustCommand.BeginExecuteReader();
// Retrieving orders list asynchronously
OrdersASyncResult = OrdersCommand.BeginExecuteReader();
CustWHandle = CustAsyncResult.AsyncWaitHandle;
OrdersWHandle = OrdersASyncResult.AsyncWaitHandle;
// Filling Wait Handles array with the two wait handles we
// are going to use in this code
WHandles[0] = CustWHandle;
WHandles[1] = OrdersWHandle;
System.Threading.WaitHandle.WaitAll(WHandles);
CustReader = CustCommand.EndExecuteReader(CustAsyncResult);
OrdersReader = OrdersCommand.EndExecuteReader(OrdersASyncResult);
gvCustomers.DataSource = CustReader;
gvCustomers.DataBind();
gvOrders.DataSource = OrdersReader;
gvOrders.DataBind();
DBCon.Close();
}
</script>
上面的代码显示了wait方法的优雅,然而在ADO.NET2.0中这不是最有效的代码。代码允许你可以在把结果集绑到到各自的GridView控件之前等待两个异步过程的调用结束。你可以修改代码如下所示以获取更好的效率。用WaitAny方法来代替WaitAll。WaitAny方法可以尽快处理一个调用结束的异步过程而不用等待其他过程结束。使用WaitAny方法你依然可以添加循环来保证其他过程调用结束后得到处理。WaitAny方法返回一个整数表示在WaitHanle数组中调用结束的那个异步过程的索引。使用这个返回值你可以得到wait句柄来处理一个已经结束的异步过程所返回的结果。
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Data” %>
<%@ Import Namespace=”System.Data.SqlClient” %>
<%@ Import Namespace=”System.Configuration” %>
<script runat=”server”>
protected void Page_Load(object sender, EventArgs e)
{
SqlConnection DBCon;
SqlCommand OrdersCommand = new SqlCommand();
SqlCommand CustCommand = new SqlCommand();
SqlDataReader OrdersReader;
SqlDataReader CustReader;
IAsyncResult OrdersASyncResult;
IAsyncResult CustAsyncResult;
int WHIndex;
System.Threading.WaitHandle[] WHandles =
new System.Threading.WaitHandle[1];
System.Threading.WaitHandle OrdersWHandle;
System.Threading.WaitHandle CustWHandle;
DBCon = new SqlConnection();
DBCon.ConnectionString =
ConfigurationManager.ConnectionStrings[“DSN_NorthWind”].ConnectionString;
CustCommand.CommandText =
“ SELECT * FROM Customers WHERE CompanyName = ‘Alfreds Futterkiste’ “;
CustCommand.CommandType = CommandType.Text;
CustCommand.Connection = DBCon;
OrdersCommand.CommandText =
“ SELECT Customers.CompanyName, Customers.ContactName, “ +
“ Orders.OrderID, Orders.OrderDate, “ +
“ Orders.RequiredDate, Orders.ShippedDate “ +
“ FROM Orders, Customers “ +
“ WHERE Orders.CustomerID = Customers.CustomerID “ +
“ AND Customers.CompanyName = ‘Alfreds Futterkiste’ “ +
“ ORDER BY Customers.CompanyName, Customers.ContactName “;
OrdersCommand.CommandType = CommandType.Text;
OrdersCommand.Connection = DBCon;
// Opening the database connection
DBCon.Open();
// Retrieving customer information asynchronously
CustAsyncResult = CustCommand.BeginExecuteReader();
// Retrieving orders list asynchronously
OrdersASyncResult = OrdersCommand.BeginExecuteReader();
CustWHandle = CustAsyncResult.AsyncWaitHandle;
OrdersWHandle = OrdersASyncResult.AsyncWaitHandle;
// Filling Wait Handles array with the two wait handles we
// are going to use in this code
WHandles[0] = CustWHandle;
WHandles[1] = OrdersWHandle;
// Looping 2 times because there are 2 wait handles
// in the array
for (int Index = 0; Index < 2; Index++ )
{
// We are only waiting for any of the two
// asynchronous process to finish running
WHIndex = System.Threading.WaitHandle.WaitAny(WHandles);
// The return value from the WaitAny method is
// the array index of the Wait Handle that just
// finsihed running
switch (WHIndex)
{
case 0:
CustReader = CustCommand.EndExecuteReader(CustAsyncResult);
gvCustomers.DataSource = CustReader;
gvCustomers.DataBind();
break;
case 1:
OrdersReader =
OrdersCommand.EndExecuteReader(OrdersASyncResult);
gvOrders.DataSource = OrdersReader;
gvOrders.DataBind();
break;
}
}
// Closing connection
DBCon.Close();
}
</script>
下面的代码用内嵌sql语句方式通过调用BeginExecuteReader取出前五条数据,并且把callback的代理传给这个方法。不需要其他的处理,当这个异步调用结束时回调函数将被触发,并且取出结果集显示在屏幕上。
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Data” %>
<%@ Import Namespace=”System.Data.SqlClient” %>
<%@ Import Namespace=”System.Configuration” %>
<script runat=”server”>
protected void Page_Load(object sender, EventArgs e)
{
SqlConnection DBCon;
SqlCommand Command = new SqlCommand();
SqlAsyncResult ASyncResult;
DBCon = new SqlConnection();
Command = new SqlCommand();
DBCon.ConnectionString =
ConfigurationManager.ConnectionStrings[“DSN_NorthWind”].ConnectionString;
// Selecting top 5 records from the Orders table
Command.CommandText =
“SELECT TOP 5 Customers.CompanyName, Customers.ContactName, “ +
“ Orders.OrderID, Orders.OrderDate, “ +
“ Orders.RequiredDate, Orders.ShippedDate “ +
“ FROM Orders, Customers “ +
“ WHERE Orders.CustomerID = Customers.CustomerID “ +
“ ORDER BY Customers.CompanyName, Customers.ContactName “;
Command.CommandType = CommandType.Text;
Command.Connection = DBCon;
DBCon.Open();
// Starting the asynchronous processing
AsyncResult = Command.BeginExecuteReader(new AsyncCallback(CBMethod),
CommandBehavior.CloseConnection);
}
public void CBMethod(SQLAsyncResult ar)
{
SqlDataReader OrdersReader;
// Retrieving result from the asynchronous process
OrdersReader = ar.EndExecuteReader(ar);
// Displaying result on the screen
gvOrders.DataSource = OrdersReader;
gvOrders.DataBind();
}
</script>
回调函数可以让你在代码的其他部分来处理命令执行的结果。这种特性在命令执行过程比一般的长但是你不想让用户等待过程调用结束时会非常有用。