當您的應用程式一次只處理一個非同步作業時,用於處理非同步作業的回呼和輪詢模型就很實用。 等候模型提供更有彈性的方式來處理多個非同步作業。 等候模型有兩種,這兩者都是針對用於實作它們的 WaitHandle 方法而命名:等候 (任何) 模型和等候 (全部) 模型。
若要使用任一種等候模型,您必須使用 BeginExecuteNonQuery、BeginExecuteReader 或 BeginExecuteXmlReader 方法所傳回之 IAsyncResult 物件的 AsyncWaitHandle 屬性。 WaitAny 和 WaitAll 方法都會要求您傳送 WaitHandle 物件作為引數,並於陣列中群組在一起。
這兩種等候方法都會監視非同步作業,等待完成。 WaitAny 方法會等待作業完成或逾時。當您知道有作業完成時,就能處理其結果,並繼續等待下一個作業完成或逾時。WaitAll 方法會等待眾多 WaitHandle 執行個體的所有處理序完成或逾時,然後才會繼續。
當您需要在不同伺服器上執行某個長度的多個作業時,或者,當您伺服器的功能強大到足以同時處理所有查詢時,等候模型的優點最為顯著。 在這裡顯示的範例中,三個查詢會藉由將不同長度的 WAITFOR 命令新增到無關緊要的 SELECT 查詢來模擬較長的處理序。
範例:Wait (Any) 模式
下列範例說明等候 (任何) 模型。 一旦啟動三個非同步處理序之後,即會呼叫 WaitAny 方法,以等候其中任一個處理序完成。 當每個處理序完成時,即會呼叫 EndExecuteReader 方法,並讀取產生的 SqlDataReader 物件。 此時,真實世界的應用程式可能會使用 SqlDataReader 來填入頁面的一部分。 在這個簡單範例中,會將處理序完成的時間新增至對應到該處理序的文字方塊。 整體而言,文字方塊中的時間可說明:每次處理序完成時,都會執行程式碼。
若要設定此範例,請建立新的 ASP.NET 網站專案。 在頁面上放置一個 Button 控制項和四個 TextBox 控制項 (接受每個控制項的預設名稱)。
將下列程式碼新增至表單的類別,並視您的環境需要修改連接字串。
// 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 Microsoft.Data.SqlClient;
// Add this code to the page's class
string 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();
}
範例:Wait (All) 模式
下列範例說明等候 (全部) 模型。 一旦啟動三個非同步處理序之後,即會呼叫 WaitAll 方法,以等候處理序完成或逾時。
如同等候 (任何) 模型的範例,會將處理序完成的時間新增至對應到該處理序的文字方塊。 同樣地,文字方塊中的時間可說明:只有當所有處理序都完成之後,才會執行 WaitAny 方法後面的程式碼。
若要設定此範例,請建立新的 ASP.NET 網站專案。 在頁面上放置一個 Button 控制項和四個 TextBox 控制項 (接受每個控制項的預設名稱)。
將下列程式碼新增至表單的類別,並視您的環境需要修改連接字串。
// 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 Microsoft.Data.SqlClient;
// Add this code to the page's class
string 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 execute UPDATE queries that result in
// no change to the data. WAITFOR
// commands are used to emulate long-running processes
// that complete after different periods of time.
string commandText1 =
"UPDATE Production.Product " +
"SET ReorderPoint = ReorderPoint + 1 " +
"WHERE ReorderPoint Is Not Null;" +
"WAITFOR DELAY '0:0:01';" +
"UPDATE Production.Product " +
"SET ReorderPoint = ReorderPoint - 1 " +
"WHERE ReorderPoint Is Not Null";
string commandText2 =
"UPDATE Production.Product " +
"SET ReorderPoint = ReorderPoint + 1 " +
"WHERE ReorderPoint Is Not Null;" +
"WAITFOR DELAY '0:0:05';" +
"UPDATE Production.Product " +
"SET ReorderPoint = ReorderPoint - 1 " +
"WHERE ReorderPoint Is Not Null";
string commandText3 =
"UPDATE Production.Product " +
"SET ReorderPoint = ReorderPoint + 1 " +
"WHERE ReorderPoint Is Not Null;" +
"WAITFOR DELAY '0:0:10';" +
"UPDATE Production.Product " +
"SET ReorderPoint = ReorderPoint - 1 " +
"WHERE ReorderPoint Is Not Null";
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.BeginExecuteNonQuery();
WaitHandle waitHandle1 = result1.AsyncWaitHandle;
connection2.Open();
SqlCommand command2 =
new SqlCommand(commandText2, connection2);
IAsyncResult result2 = command2.BeginExecuteNonQuery();
WaitHandle waitHandle2 = result2.AsyncWaitHandle;
connection3.Open();
SqlCommand command3 =
new SqlCommand(commandText3, connection3);
IAsyncResult result3 = command3.BeginExecuteNonQuery();
WaitHandle waitHandle3 = result3.AsyncWaitHandle;
WaitHandle[] waitHandles = {
waitHandle1, waitHandle2, waitHandle3
};
bool result;
// WaitAll waits for all of the processes to
// complete. The return value is True if the processes
// all completed successfully, False if any process
// timed out.
result = WaitHandle.WaitAll(waitHandles, 60000, false);
if(result)
{
long rowCount1 =
command1.EndExecuteNonQuery(result1);
TextBox1.Text = "Completed " +
System.DateTime.Now.ToLongTimeString();
long rowCount2 =
command2.EndExecuteNonQuery(result2);
TextBox2.Text = "Completed " +
System.DateTime.Now.ToLongTimeString();
long rowCount3 =
command3.EndExecuteNonQuery(result3);
TextBox3.Text = "Completed " +
System.DateTime.Now.ToLongTimeString();
}
else
{
throw new Exception("Timeout");
}
}
catch (Exception ex)
{
TextBox4.Text = ex.ToString();
}
connection1.Close();
connection2.Close();
connection3.Close();
}