在您的应用程序一次只处理一个异步操作时,用于处理异步操作的回调和轮询模型十分有用。等待模型提供了一种更灵活的方式来处理多个异步操作。有两种等待模型,是根据用于实现它们的 WaitHandle 方法命名的:等待(任何)模型和等待(所有)模型。
要使用上述任一等待模型,您需要使用 BeginExecuteNonQuery、BeginExecuteReader 或 BeginExecuteXmlReader 方法返回的 IAsyncResult 对象的 AsyncWaitHandle 属性。WaitAny 和 WaitAll 方法都要求您将多个 WaitHandle 对象一起组合在一个数组中,作为一个参数发送。
这两种等待方法都监控异步操作,等待操作完成。WaitAny 方法等待任何操作完成或超时。一旦您知道某一特定操作完成后,就可以处理其结果,然后继续等待下一个操作完成或超时。WaitAll 方法等待 WaitHandle 实例数组中的所有进程都完成或超时后,再继续。
当您需要在不同服务器上运行时间长度不等的多个操作时,或者在服务器的性能强大到足以同时处理所有查询时,等待模型的优点可以最大地发挥。在此处提供的示例中,通过将长度不等的多个 WAITFOR 命令添加到不重要的 SELECT 查询,三个查询模拟长进程。
下面的示例说明等待(任何)模型。一旦启动了三个异步进程后,就调用 WaitAny 方法以等待其中任何一个进程完成。在每个进程完成后,调用 EndExecuteReader 方法并读取结果 SqlDataReader 对象。在此时,实际的应用程序将很可能使用 SqlDataReader 填充该页的某个部分。在这个简单的示例中,进程完成时间被添加到与该进程相对应的文本框中。合起来看,文本框中的这些时间说明以下结论:每次进程完成后都执行代码。
要设置此示例,请创建一个新的 ASP.NET 网站项目。将一个 Button 控件和四个 TextBox 控件放置于页面上(接受每个控件的默认名称)。
在窗体的类中添加以下代码,根据环境的需要修改连接字符串。
[C#]
// Add the following using statements, if they are not already there.using System;using System.Data;using System.Configuration;using System.Web;using System.Web.Security;using System.Web.UI;using System.Web.UI.WebControls;using System.Web.UI.WebControls.WebParts;using System.Web.UI.HtmlControls;using System.Threading;using System.Data.SqlClient;// Add this code to the page's classstring GetConnectionString() // To avoid storing the connection string in your code, // you can retrieve it from a configuration file. // If you have not included "Asynchronous Processing=true" // in the connection string, the command will not be able // to execute asynchronously.{ return "Data Source=(local);Integrated Security=SSPI;" + "Initial Catalog=AdventureWorks;" + "Asynchronous Processing=true";}void Button1_Click(object sender, System.EventArgs e){ // In a real-world application, you might be connecting to // three different servers or databases. For the example, // we connect to only one. SqlConnection connection1 = new SqlConnection(GetConnectionString()); SqlConnection connection2 = new SqlConnection(GetConnectionString()); SqlConnection connection3 = new SqlConnection(GetConnectionString()); // To keep the example simple, all three asynchronous // processes select a row from the same table. WAITFOR // commands are used to emulate long-running processes // that complete after different periods of time. string commandText1 = "WAITFOR DELAY '0:0:01';" + "SELECT * FROM Production.Product " + "WHERE ProductNumber = 'BL-2036'"; string commandText2 = "WAITFOR DELAY '0:0:05';" + "SELECT * FROM Production.Product " + "WHERE ProductNumber = 'BL-2036'"; string commandText3 = "WAITFOR DELAY '0:0:10';" + "SELECT * FROM Production.Product " + "WHERE ProductNumber = 'BL-2036'"; try // For each process, open a connection and begin // execution. Use the IAsyncResult object returned by // BeginExecuteReader to add a WaitHandle for the // process to the array. { connection1.Open(); SqlCommand command1 = new SqlCommand(commandText1, connection1); IAsyncResult result1 = command1.BeginExecuteReader(); WaitHandle waitHandle1 = result1.AsyncWaitHandle; connection2.Open(); SqlCommand command2 = new SqlCommand(commandText2, connection2); IAsyncResult result2 = command2.BeginExecuteReader(); WaitHandle waitHandle2 = result2.AsyncWaitHandle; connection3.Open(); SqlCommand command3 = new SqlCommand(commandText3, connection3); IAsyncResult result3 = command3.BeginExecuteReader(); WaitHandle waitHandle3 = result3.AsyncWaitHandle; WaitHandle[] waitHandles = { waitHandle1, waitHandle2, waitHandle3 }; int index; for (int countWaits = 0; countWaits <= 2; countWaits++) { // WaitAny waits for any of the processes to // complete. The return value is either the index // of the array element whose process just // completed, or the WaitTimeout value. index = WaitHandle.WaitAny(waitHandles, 60000, false); // This example doesn't actually do anything with // the data returned by the processes, but the // code opens readers for each just to demonstrate // the concept. // Instead of using the returned data to fill the // controls on the page, the example adds the time // the process was completed to the corresponding // text box. switch (index) { case 0: SqlDataReader reader1; reader1 = command1.EndExecuteReader(result1); if (reader1.Read()) { TextBox1.Text = "Completed " + System.DateTime.Now.ToLongTimeString(); } reader1.Close(); break; case 1: SqlDataReader reader2; reader2 = command2.EndExecuteReader(result2); if (reader2.Read()) { TextBox2.Text = "Completed " + System.DateTime.Now.ToLongTimeString(); } reader2.Close(); break; case 2: SqlDataReader reader3; reader3 = command3.EndExecuteReader(result3); if (reader3.Read()) { TextBox3.Text = "Completed " + System.DateTime.Now.ToLongTimeString(); } reader3.Close(); break; case WaitHandle.WaitTimeout: throw new Exception("Timeout"); break; } } } catch (Exception ex) { TextBox4.Text = ex.ToString(); } connection1.Close(); connection2.Close(); connection3.Close();} |