共用方式為


使用等候控制代碼的 ASP.NET 應用程式 (ADO.NET)

更新: November 2007

當應用程式一次只處理一個非同步作業時,用於處理非同步作業的回呼及輪詢模式會很有用。等候模式可提供更靈活的方式來處理多個非同步作業。有兩種等候模式,這兩種模式都是針對用於實作它們的 WaitHandle 方法而命名:等候 (任何) 模式及等候 (所有) 模式。

若要使用其中一種等候模式,您需要使用由 BeginExecuteNonQueryBeginExecuteReaderBeginExecuteXmlReader 方法傳回之 IAsyncResult 物件的 AsyncWaitHandle 屬性。WaitAnyWaitAll 方法都會要求您傳送 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();
}

請參閱

其他資源

非同步作業 (ADO.NET)