Share via


XML Web サービスとの非同期通信

XML Web サービスとの非同期通信では、Microsoft .NET Framework の別の部分が使用する非同期デザイン パターンに従います。ただし、非同期デザイン パターンの詳細を確認する前に、XML Web サービスは、非同期に呼び出された非同期要求を処理するために具体的な記述を必要としないことに注意してください。Web サービス記述言語ツール (Wsdl.exe) を使用してクライアント用に作成されたプロキシ クラスは、XML Web サービス メソッドを非同期に呼び出すためのメソッドを自動的に作成します。これは、XML Web サービス メソッドの同期実装しかない場合でも同様です。

.NET Framework 非同期メソッド呼び出しのデザイン パターン

.NET Framework によって指定される、メソッドを非同期で呼び出すためのデザイン パターンは、同期メソッドごとに 2 つの非同期メソッドを用意することを前提とします。各同期メソッドに対して、Begin 非同期メソッドと End 非同期メソッドを用意します。Begin メソッドはクライアントによって呼び出され、メソッドの呼び出しを開始します。つまり、クライアントは Begin メソッドに対して、メソッド呼び出しの処理を開始し、すぐに制御を返すように指示します。End メソッドはクライアントによって呼び出され、XML Web サービス メソッドの呼び出しによって実行された処理結果を取得します。

End ** メソッドを呼び出すタイミングをクライアントが判断する方法について説明します。.NET Framework で定義されているように、このタイミングを判断する機能をクライアントに実装するには 2 つの方法があります。最初の方法では、コールバック関数を Begin メソッドに渡し、メソッドの処理が完了したときにそのコールバック関数が呼び出されるようにします。2 番目の方法では、WaitHandle クラスのいずれかのメソッドを使用して、メソッドの処理が完了するまでクライアントを待機させます。クライアントが 2 番目の方法を実装している場合に、Begin ** メソッドを呼び出すと、戻り値が XML Web サービス メソッドによって指定されたデータ型ではなく、IAsyncResult インターフェイスを実装している型になります。IAsyncResult インターフェイスには、WaitHandle 型の AsyncWaitHandle プロパティが含まれています。WaitHandle は、同期オブジェクトが WaitHandle.WaitOneWaitAny、および WaitAll の意味のシグナル状態になるまで待機するメソッドを実装します。同期オブジェクトがシグナル状態になった場合は、指定したリソースを待機するスレッドがそのリソースにアクセスできるようになったことがわかります。XML Web サービス クライアントが待機メソッドを使用して XML Web サービス メソッドを 1 つだけ非同期で呼び出す場合、XML Web サービス クライアントは WaitOne を呼び出し、該当の XML Web サービス メソッドの処理が完了するまで待機できます。

クライアントが XML Web サービスとの非同期通信に上の 2 つの方法のどちらを使用するかにかかわらず、送受信される SOAP メッセージは同期通信の場合と変わりません。つまり、ネットワーク経由で送受信される SOAP 要求と SOAP 応答は 1 組だけです。プロキシ クラスでこれを実現するには、Begin メソッドの呼び出しにクライアントが使用するスレッドとは異なるスレッドを使用して SOAP 応答を処理します。したがって、クライアントではそのスレッドで他の作業の実行を続行でき、同時にプロキシ クラスでは SOAP 応答の受信および処理を行うことができます。

非同期でメソッドを呼び出す XML Web サービス クライアントの実装

ASP.NET を使用して作成した XML Web サービス クライアントから XML Web サービスに非同期で呼び出しを行うためのインフラストラクチャは、.NET Framework と、Web サービス記述言語ツール (Wsdl.exe) によって作成されたプロキシ クラスに組み込まれています。非同期呼び出しのデザイン パターンは .NET Framework によって定義され、プロキシ クラスが非同期で XML Web サービス メソッドと通信するための機構を提供します。Wsdl.exe を使用して XML Web サービス用のプロキシ クラスを作成すると、その XML Web サービスの各パブリック XML Web サービス メソッドに 3 つのメソッドが作成されます。この 3 つのメソッドについて次の表で説明します。

プロキシ クラスでのメソッド名 説明
<NameOfWebServiceMethod> <NameOfWebServiceMethod> という名前の XML Web サービス メソッドにメッセージを同期的に送信します。
Begin<NameOfWebServiceMethod> <NameOfWebServiceMethod> という名前の XML Web サービス メソッドとの非同期メッセージ通信を開始します。
End<NameOfWebServiceMethod> <NameOfWebServiceMethod> という名前の XML Web サービス メソッドとの非同期メッセージ通信を終了し、その XML Web サービス メソッドから完了したメッセージを取得します。

処理を終了するまでに比較的長時間を要する XML Web サービス メソッドを次のコード例に示します。たとえば、非同期で XML Web サービス メソッドを呼び出すように XML Web サービス クライアントを設定する場合には、このコード例が適しています。

<%@ WebService Language="C#" Class="PrimeFactorizer" %>

using System;
using System.Collections;
using System.Web.Services;

class PrimeFactorizer {

[WebMethod]
public long[] Factorize(long factorizableNum){
      ArrayList outList = new ArrayList();
      long i = 0;
      int j;
      try{
            long Check = factorizableNum;
      
            //Go through every possible integer
            //factor between 2 and factorizableNum / 2.
            //Thus, for 21, check between 2 and 10.
            for (i = 2; i < (factorizableNum / 2); i++){
                  while(Check % i == 0){
                        outList.Add(i);
                        Check = (Check/i);
                  }
            }
            //Double-check to see how many prime factors have been added.
            //If none, add 1 and the number.
            j = outList.Count;
            if (j == 0) {
                  outList.Add(1);
                  outList.Add(factorizableNum);
            }
            j = outList.Count;
            
            //Return the results and
            //create an array to hold them.
            long[] primeFactor = new long[j];
            for (j = 0; j < outList.Count; j++){
                  //Pass the values one by one, making sure
                  //to convert them to type ulong.
                  primeFactor[j] = Convert.ToInt64(outList[j]);
            }
            return primeFactor;
      }
      catch (Exception) {
            return null;
      }
}
} 
[Visual Basic]
<%@ WebService Class="PrimeFactorizer" Language="VB" %>
Imports System
Imports System.Collections
Imports System.Web.Services

Public Class PrimeFactorizer   
    <WebMethod> _
    Public Function Factorize(factorizableNum As Long) As Long()
        Dim outList As New ArrayList()
        Dim i As Long = 0
        Dim j As Integer
        Try
            Dim Check As Long = factorizableNum
            
            'Go through every possible integer
            'factor between 2 and factorizableNum / 2.
            'Thus, for 21, check between 2 and 10.
            For i = 2 To CLng(factorizableNum / 2) - 1
                While Check Mod i = 0
                    outList.Add(i)
                    Check = CLng(Check / i)
                End While
            Next i
            'Double-check to see how many prime factors have been added.
            'If none, add 1 and the number.
            j = outList.Count
            If j = 0 Then
                outList.Add(1)
                outList.Add(factorizableNum)
            End If
            j = outList.Count
            
            'Return the results and
            'create an array to hold them.
            Dim primeFactor(j - 1) As Long
            For j = 0 To outList.Count - 1
                'Pass the values one by one, making sure
                'to convert them to type ulong.
                primeFactor(j) = CLng(outList(j))
            Next j
            Return primeFactor
        Catch
            Return Nothing
        End Try
    End Function
End Class

上の XML Web サービス メソッドに対して Wsdl.exe によって生成されるプロキシ クラスの一部を次のコード例に示します。BeginFactorize メソッドと EndFactorize メソッドは、Factorize XML Web サービス メソッドと非同期で通信するために使用されています。

public class PrimeFactorizer : System.Web.Services.Protocols.SoapHttpClientProtocol {
        
        public long[] Factorize(long factorizableNum) {
            object[] results = this.Invoke("Factorize", new object[] {
                        factorizableNum});
            return ((long[])(results[0]));
        }
        
        public System.IAsyncResult BeginFactorize(long factorizableNum, System.AsyncCallback callback, object asyncState) {
            return this.BeginInvoke("Factorize", new object[] {
                        factorizableNum}, callback, asyncState);
        }
        
        public long[] EndFactorize(System.IAsyncResult asyncResult) {
            object[] results = this.EndInvoke(asyncResult);
            return ((long[])(results[0]));
        }
    }

XML Web サービス メソッドと非同期で通信するための方法は 2 つあります。XML Web サービス メソッドと非同期で通信し、コールバック関数を使用して XML Web サービス メソッドからその結果を取得する方法を次に示します。

using System;
using System.Runtime.Remoting.Messaging;
using MyFactorize;

class TestCallback
 {           
      public static void Main(){
            long factorizableNum = 12345;
            PrimeFactorizer pf = new PrimeFactorizer();

            //Instantiate an AsyncCallback delegate to use as a parameter
            //in the BeginFactorize method.
            AsyncCallback cb = new AsyncCallback(TestCallback.FactorizeCallback);

          // Begin the Async call to Factorize, passing in our
          // AsyncCalback delegate and a reference
          // to our instance of PrimeFactorizer.
            IAsyncResult ar = pf.BeginFactorize(factorizableNum, cb, pf);
            
            // Keep track of the time it takes to complete the async call
            // as the call proceeds.
         int start = DateTime.Now.Second;
         int currentSecond = start;
         while (ar.IsCompleted == false){
            if (currentSecond < DateTime.Now.Second) {
                  currentSecond = DateTime.Now.Second;
                  Console.WriteLine("Seconds Elapsed..." + (currentSecond - start).ToString() );
            }
         }
         // Once the call has completed, you need a method to ensure the
         // thread executing this Main function 
         // doesn't complete prior to the call-back function completing.
         Console.Write("Press Enter to quit");
         int quitchar = Console.Read();
      }
      // Set up a call-back function that is invoked by the proxy class
      // when the asynchronous operation completes.
      public static void FactorizeCallback(IAsyncResult ar)
      {
          // You passed in our instance of PrimeFactorizer in the third
          // parameter to BeginFactorize, which is accessible in the
          // AsyncState property.
          PrimeFactorizer pf = (PrimeFactorizer) ar.AsyncState;
          long[] results;

          // Get the completed results.
            results = pf.EndFactorize(ar);
          
          //Output the results.
            Console.Write("12345 factors into: ");
            int j;
            for (j = 0; j<results.Length;j++){
                  if (j == results.Length - 1)
                      Console.WriteLine(results[j]);
                  else 
                      Console.Write(results[j] + ", ");
            }
      }
}
[Visual Basic]
Imports System
Imports System.Runtime.Remoting.Messaging
Imports MyFactorize

Public Class TestCallback
      Public Shared Sub Main()
            Dim factorizableNum As Long = 12345
            Dim pf As PrimeFactorizer = new PrimeFactorizer()

            'Instantiate an AsyncCallback delegate to use as a parameter
            ' in the BeginFactorize method.
            Dim cb as AsyncCallback 
          cb = new AsyncCallback(AddressOf TestCallback.FactorizeCallback)

          ' Begin the Async call to Factorize, passing in the
          ' AsyncCallback delegate and a reference to our instance
          ' of PrimeFactorizer.
          Dim ar As IAsyncResult = pf.BeginFactorize(factorizableNum, _
                                                     cb, pf)
            
          ' Keep track of the time it takes to complete the async call as
          ' the call proceeds.
         Dim start As Integer = DateTime.Now.Second
         Dim currentSecond As Integer = start
         Do while (ar.IsCompleted = false)
            If (currentSecond < DateTime.Now.Second) Then
                  currentSecond = DateTime.Now.Second
                  Console.WriteLine("Seconds Elapsed..." + (currentSecond - start).ToString() )
            End If
         Loop

         ' Once the call has completed, you need a method to ensure the
         ' thread executing this Main function 
         ' doesn't complete prior to the callback function completing.
         Console.Write("Press Enter to quit")
         Dim quitchar As Integer = Console.Read()
      End Sub

      ' Set up the call-back function that is invoked by the proxy 
      ' class when the asynchronous operation completes.
      Public Shared Sub FactorizeCallback(ar As IAsyncResult)
      
          ' You passed in the instance of PrimeFactorizer in the third
          ' parameter to BeginFactorize, which is accessible in the
          ' AsyncState property.

          Dim pf As PrimeFactorizer = ar.AsyncState
          Dim results() as Long

          ' Get the completed results.
            results = pf.EndFactorize(ar)
          
          'Output the results.
            Console.Write("12345 factors into: ")
            Dim j as Integer
            For j = 0 To results.Length - 1
                  If  j = (results.Length - 1) Then
                      Console.WriteLine(results(j) )
                  Else 
                      Console.Write(results(j).ToString + ", ")
                    End If
          Next j         
      End Sub      
End Class

XML Web サービス メソッドと非同期で通信してから、同期オブジェクトを使用して処理が終了するまで待機する方法を次のコード例に示します。

// -----------------------------------------------------------------------// Async Variation 2.
// Asynchronously invoke the Factorize method, 
//without specifying a call back.
using System;
using System.Runtime.Remoting.Messaging;
// MyFactorize, is the name of the namespace in which the proxy class is
// a member of for this sample.
using MyFactorize;  

class TestCallback
 {          
      public static void Main(){
            long factorizableNum = 12345;
            PrimeFactorizer pf = new PrimeFactorizer();

          // Begin the Async call to Factorize.
            IAsyncResult ar = pf.BeginFactorize(factorizableNum, null, null);

          // Wait for the asynchronous operation to complete.
          ar.AsyncWaitHandle.WaitOne();

          // Get the completed results.
          long[] results;     
          results = pf.EndFactorize(ar);
          
          //Output the results.
            Console.Write("12345 factors into: ");
            int j;
            for (j = 0; j<results.Length;j++){
                  if (j == results.Length - 1)
                      Console.WriteLine(results[j]);
                  else 
                      Console.Write(results[j] + ", ");
            }
        }
}
[Visual Basic]
Imports System
Imports System.Runtime.Remoting.Messaging
Imports MyFactorize     ' Proxy class namespace

Public Class TestCallback
      Public Shared Sub Main()
            Dim factorizableNum As Long = 12345
            Dim pf As PrimeFactorizer = new PrimeFactorizer()

          ' Begin the Async call to Factorize.
            Dim ar As IAsyncResult = pf.BeginFactorize(factorizableNum, Nothing, Nothing)

          ' Wait for the asynchronous operation to complete.
          ar.AsyncWaitHandle.WaitOne()

          ' Get the completed results.
          Dim results() as Long
          results = pf.EndFactorize(ar)
          
          'Output the results.
            Console.Write("12345 factors into: ")
            Dim j as Integer
            For j = 0 To results.Length - 1
                  If  j = (results.Length - 1) Then
                      Console.WriteLine(results(j) )
                  Else 
                      Console.Write(results(j).ToString + ", ")
                    End If
          Next j         
      End Sub
End Class

FactorizeCallback が、同期/スレッド アフィニティのコンテキストを必要とするコンテキスト バインド クラスの場合、このコールバック関数はコンテキスト ディスパッチャ インフラストラクチャを通じてディスパッチされます。つまり、このようなコンテキストでは、コールバック関数がその呼び出し元に対して非同期で実行される可能性があります。これが、メソッド シグネチャの一方向修飾子の正確なセマンティクスです。つまり、このようなメソッド呼び出しは、呼び出し元に対して同期的または非同期的に実行されるため、呼び出し元は、実行制御が戻された場合でも呼び出しが完了したことを前提にできません。

また、非同期操作が完了する前に EndInvoke を呼び出すと、呼び出し元がブロックされます。このメソッドを同じ AsyncResult を指定して 2 回目に呼び出した場合の動作は不定です。

Cancel メソッドは、指定したタイムアウト時間が経過した後で、メソッドの処理をキャンセルする要求です。これは、クライアントからの要求で、サーバーで処理することをお勧めします。クライアントでは、メソッドをキャンセルしたメッセージを受信した後に、サーバーがそのメソッドの処理を中断したかどうかについて想定しないようにしてください。サーバーがファイル オブジェクトなどのリソースを使用している可能性があるため、クライアントではリソースを破棄しないことをお勧めします。サーバーが処理を完了し、クライアントが提供したリソースが不要になった場合には、IAsyncResult インスタンスの IsCompleted プロパティが true に設定されます。したがって、IsCompleted プロパティが true に設定された後は、クライアント側でも安心してリソースを破棄できます。

参照

XML Web サービス クライアントの作成 | XML Web サービスの探索 | XML Web サービスのクライアントの作成 | ASP.NET を使用して作成した既存の XML Web サービスの調査 | ブラウザから XML Web サービスへのアクセス