使用等候控制代碼的 ASP.NET 應用程式 (ADO.NET)
更新: November 2007
當應用程式一次只處理一個非同步作業時,用於處理非同步作業的回呼及輪詢模式會很有用。等候模式可提供更靈活的方式來處理多個非同步作業。有兩種等候模式,這兩種模式都是針對用於實作它們的 WaitHandle 方法而命名:等候 (任何) 模式及等候 (所有) 模式。
若要使用其中一種等候模式,您需要使用由 BeginExecuteNonQuery、BeginExecuteReader 或 BeginExecuteXmlReader 方法傳回之 IAsyncResult 物件的 AsyncWaitHandle 屬性。WaitAny 與 WaitAll 方法都會要求您傳送 WaitHandle 物件做為引數,一起群組在陣列中。
這兩種等候方法都會監視非同步作業,並且等候作業完成。WaitAny 方法會等候任意一項作業完成或逾時。一旦您知道某項特定作業已完成,就可處理其結果,然後繼續等候下一項作業完成或逾時。WaitAll 方法會等候 WaitHandle 執行個體陣列中的所有處理序完成或逾時後才繼續。
當您需要在不同伺服器上執行一定長度的多項作業,或您的伺服器功能足以同時處理所有查詢時,最可以突顯出等候模型的優點。在此處顯示的範例中,三個查詢藉由將不同長度的 WAITFOR 命令加入不連貫的 SELECT 查詢,模擬長處理序。
範例:等候 (任何) 模式
下列範例說明等候 (任何) 模式。啟動三個非同步處理序後,會呼叫 WaitAny 方法,以等候其中任何一項處理序完成。每個處理序完成時,都會呼叫 EndExecuteReader 方法,並讀取產生的 SqlDataReader 物件。此時,實際應用程式很可能使用 SqlDataReader 來填入一部分頁面。在此簡單範例中,會將處理序完成的時間加入與該處理序對應的文字方塊中。統一來看,文字方塊中的時間可說明:每次處理序完成時,都會執行程式碼。
若要設定此範例,請建立新的 ASP.NET 網站專案。將一個 Button 控制項與四個 TextBox 控制項置於頁面上 (接受每個控制項的預設名稱)。
將下列程式碼加入表單類別,並視環境需要修改連接字串。
[Visual Basic]
' Add these to the top of the class
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Threading
' Add this code to the page's class:
Private Function GetConnectionString() As String
' 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"
End Function
Sub Button1_Click( _
ByVal sender As Object, ByVal e As System.EventArgs)
' In a real-world application, you might be connecting to
' three different servers or databases. For the example,
' we connect to only one.
Dim connection1 As New SqlConnection(GetConnectionString())
Dim connection2 As New SqlConnection(GetConnectionString())
Dim connection3 As 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.
Dim commandText1 As String = _
"WAITFOR DELAY '0:0:01';" & _
"SELECT * FROM Production.Product " & _
"WHERE ProductNumber = 'BL-2036'"
Dim commandText2 As String = _
"WAITFOR DELAY '0:0:05';" & _
"SELECT * FROM Production.Product " & _
"WHERE ProductNumber = 'BL-2036'"
Dim commandText3 As String = _
"WAITFOR DELAY '0:0:10';" & _
"SELECT * FROM Production.Product " & _
"WHERE ProductNumber = 'BL-2036'"
Dim waitHandles(2) As WaitHandle
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()
Dim command1 As New SqlCommand(commandText1, connection1)
Dim result1 As IAsyncResult = _
command1.BeginExecuteReader()
waitHandles(0) = result1.AsyncWaitHandle
connection2.Open()
Dim command2 As New SqlCommand(commandText2, connection2)
Dim result2 As IAsyncResult = _
command2.BeginExecuteReader()
waitHandles(1) = result2.AsyncWaitHandle
connection3.Open()
Dim command3 As New SqlCommand(commandText3, connection3)
Dim result3 As IAsyncResult = _
command3.BeginExecuteReader()
waitHandles(2) = result3.AsyncWaitHandle
Dim index As Integer
For countWaits As Integer = 1 To 3
' 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.
Select Case index
Case 0
Dim reader1 As SqlDataReader
reader1 = command1.EndExecuteReader(result1)
If reader1.Read Then
TextBox1.Text = _
"Completed " & _
System.DateTime.Now.ToLongTimeString()
End If
reader1.Close()
Case 1
Dim reader2 As SqlDataReader
reader2 = command2.EndExecuteReader(result2)
If reader2.Read Then
TextBox2.Text = _
"Completed " & _
System.DateTime.Now.ToLongTimeString()
End If
reader2.Close()
Case 2
Dim reader3 As SqlDataReader
reader3 = command3.EndExecuteReader(result3)
If reader3.Read Then
TextBox3.Text = _
"Completed " & _
System.DateTime.Now.ToLongTimeString()
End If
reader3.Close()
Case WaitHandle.WaitTimeout
Throw New Exception("Timeout")
End Select
Next
Catch ex As Exception
TextBox4.Text = ex.ToString
End Try
connection1.Close()
connection2.Close()
connection3.Close()
End Sub
[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 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();
}
範例:等候 (所有) 模式
下列範例說明等候 (所有) 模式。啟動三個非同步處理序後,會呼叫 WaitAll 方法,以等候所有處理序完成或逾時。
像等候 (任何) 模式的範例一樣,會將處理序完成的時間加入與該處理序對應的文字方塊中。同樣地,文字方塊中的時間可說明:只有當所有處理序都完成之後,才會執行 WaitAny 方法後的程式碼。
若要設定此範例,請建立新的 ASP.NET 網站專案。將一個 Button 控制項與四個 TextBox 控制項置於頁面上 (接受每個控制項的預設名稱)。
將下列程式碼加入表單類別,並視環境需要修改連接字串。
[Visual Basic]
' Add these to the top of the class
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Threading
' Add this code to the page's class:
Private Function GetConnectionString() As String
' 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"
End Function
Sub Button1_Click( _
ByVal sender As Object, ByVal e As System.EventArgs)
' In a real-world application, you might be connecting to
' three different servers or databases. For the example,
' we connect to only one.
Dim connection1 As New SqlConnection(GetConnectionString())
Dim connection2 As New SqlConnection(GetConnectionString())
Dim connection3 As 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.
Dim commandText1 As String = _
"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"
Dim commandText2 As String = _
"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"
Dim commandText3 As String = _
"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"
Dim waitHandles(2) As WaitHandle
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()
Dim command1 As New SqlCommand(commandText1, connection1)
Dim result1 As IAsyncResult = _
command1.BeginExecuteNonQuery()
waitHandles(0) = result1.AsyncWaitHandle
connection2.Open()
Dim command2 As New SqlCommand(commandText2, connection2)
Dim result2 As IAsyncResult = _
command2.BeginExecuteNonQuery()
waitHandles(1) = result2.AsyncWaitHandle
connection3.Open()
Dim command3 As New SqlCommand(commandText3, connection3)
Dim result3 As IAsyncResult = _
command3.BeginExecuteNonQuery()
waitHandles(2) = result3.AsyncWaitHandle
' WaitAll waits for all of the processes to complete.
' The return value is True if all processes completed,
' False if any process timed out.
Dim result As Boolean = _
WaitHandle.WaitAll(waitHandles, 60000, False)
If result Then
Dim rowCount1 As Long = _
command1.EndExecuteNonQuery(result1)
TextBox1.Text = _
"Completed " & _
System.DateTime.Now.ToLongTimeString()
Dim rowCount2 As Long = _
command2.EndExecuteNonQuery(result2)
TextBox2.Text = _
"Completed " & _
System.DateTime.Now.ToLongTimeString()
Dim rowCount3 As Long = _
command3.EndExecuteNonQuery(result3)
TextBox3.Text = _
"Completed " & _
System.DateTime.Now.ToLongTimeString()
Else
Throw New Exception("Timeout")
End If
Catch ex As Exception
TextBox4.Text = ex.ToString
End Try
connection1.Close()
connection2.Close()
connection3.Close()
End Sub
[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 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();
}