Calling Synchronous Methods Asynchronously
目次を折りたたむ
目次を展開する
この記事の英語版を表示するには、[英語] のチェック ボックスをオンにしてください。また、テキストにマウス ポインターを合わせると、ポップアップ ウィンドウに英語のテキストを表示することもできます。
翻訳
英語

Calling Synchronous Methods Asynchronously

 

.NET Framework では、すべてのメソッドを非同期的に呼び出すことができます。 これを行うには、呼び出すメソッドと同じシグネチャを持つデリゲートを定義します。これにより、共通言語ランタイムによって、適切なシグネチャを持つ、このデリゲートの BeginInvoke メソッドと EndInvoke メソッドが自動的に定義されます。

System_CAPS_noteメモ

非同期デリゲート (具体的には BeginInvoke メソッドと EndInvoke メソッド) は、.NET Compact Framework ではサポートされていません。

BeginInvoke メソッドは、非同期呼び出しを開始します。 このメソッドは、非同期的に実行するメソッドと同じパラメーターと共に、2 つの省略可能な追加パラメーターを持っています。 最初のパラメーターは、同期呼び出しが完了したときに呼び出されるメソッドを参照する AsyncCallback デリゲートです。 2 番目のパラメーターは、コールバック メソッドに情報を渡すユーザー定義オブジェクトです。 BeginInvoke からは制御がすぐに戻り、非同期呼び出しが完了するまで待機しません。 BeginInvokeIAsyncResult を返します。これを使用して非同期呼び出しの進捗状況を監視できます。

EndInvoke メソッドは、非同期呼び出しの結果を取得します。 このメソッドは、BeginInvoke の後であればいつでも呼び出すことができます。 非同期呼び出しがまだ完了していない場合は、EndInvoke は非同期呼び出しが完了するまで呼び出し元スレッドをブロックします。 EndInvoke のパラメーターには、非同期実行するメソッドの out パラメーターと ref パラメーター (Visual Basic では <Out> ByRefByRef) と、IAsyncResult によって返された BeginInvoke が含まれます。

System_CAPS_noteメモ

Visual Studio 2005 の IntelliSense 機能によって BeginInvoke および EndInvoke のパラメーターが表示されます。 Visual Studio や類似のツールを使っていない場合や、Visual Studio 2005 で C# を使っている場合、これらのメソッドについて定義されているパラメーターについては、「Asynchronous Programming Model (APM)」をご覧ください。

このトピックのコード例では、BeginInvokeEndInvoke を使用して非同期呼び出しを行う 4 つの一般的な方法を示します。 BeginInvoke を呼び出した後、次の処理を行うことができます。

  • 何か処理を実行した後、呼び出しが完了するまでブロックする EndInvoke を呼び出します。

  • WaitHandle プロパティを使用して IAsyncResult.AsyncWaitHandle を取得し、その WaitOne メソッドを使用して WaitHandle が通知されるまで実行をブロックし、EndInvoke を呼び出します。

  • IAsyncResult によって返される BeginInvoke をポーリングして非同期呼び出しが完了したかどうかを確認した後、EndInvoke を呼び出します。

  • コールバック メソッドのデリゲートを BeginInvoke に渡します。 このメソッドは、非同期呼び出しが完了すると、ThreadPool スレッドで実行されます。 コールバック メソッドは EndInvoke を呼び出します。

System_CAPS_important重要

どの手法を使用する場合でも、常に EndInvoke を呼び出して、非同期呼び出しを完了します。

次のコード例は、長時間実行される TestMethod メソッドの非同期呼び出しを行うさまざまな方法を示します。 TestMethod メソッドはコンソール メッセージを表示して処理が開始されたことを示し、しばらくスリープした後、終了します。 TestMethod には out パラメーターがあり、BeginInvoke および EndInvoke のシグネチャへのそのようなパラメーターの追加方法を示します。 ref パラメーターは同様に処理できます。

次のコード例は、TestMethod と、AsyncMethodCaller の非同期呼び出しに使用できる TestMethod という名前のデリゲートの定義を示します。 コード例をコンパイルするには、TestMethod および AsyncMethodCaller デリゲートの定義を含める必要があります。

using System;
using System.Threading; 

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncDemo 
    {
        // The method to be executed asynchronously.
        public string TestMethod(int callDuration, out int threadId) 
        {
            Console.WriteLine("Test method begins.");
            Thread.Sleep(callDuration);
            threadId = Thread.CurrentThread.ManagedThreadId;
            return String.Format("My call time was {0}.", callDuration.ToString());
        }
    }
    // The delegate must have the same signature as the method
    // it will call asynchronously.
    public delegate string AsyncMethodCaller(int callDuration, out int threadId);
}

メソッドを非同期実行する最も簡単な方法は、デリゲートの BeginInvoke メソッドを呼び出してメソッドの実行を開始し、メイン スレッドで何かの処理を実行した後、デリゲートの EndInvoke メソッドを呼び出す方法です。 EndInvoke は非同期呼び出しが完了するまで戻らないので、呼び出し元スレッドがブロックされる場合があります。 この手法はファイルやネットワーク操作を使用するときに適しています。

System_CAPS_important重要

EndInvoke でブロックするため、ユーザー インターフェイスにサービスを提供するスレッドからは呼び出さないでください。

using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        public static void Main() 
        {
            // The asynchronous method puts the thread id here.
            int threadId;

            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // Initiate the asychronous call.
            IAsyncResult result = caller.BeginInvoke(3000, 
                out threadId, null, null);

            Thread.Sleep(0);
            Console.WriteLine("Main thread {0} does some work.",
                Thread.CurrentThread.ManagedThreadId);

            // Call EndInvoke to wait for the asynchronous call to complete,
            // and to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, result);

            Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".",
                threadId, returnValue);
        }
    }
}

/* This example produces output similar to the following:

Main thread 1 does some work.
Test method begins.
The call executed on thread 3, with return value "My call time was 3000.".
 */

WaitHandle を取得するには、AsyncWaitHandle によって返される IAsyncResultBeginInvoke プロパティを使用します。 WaitHandle は非同期呼び出しが完了すると通知され、WaitOne メソッドを呼び出すことによってこれを待機できます。

WaitHandle を使用する場合は、非同期呼び出しの完了前または完了後、EndInvoke を呼び出して結果を取得する前に、追加の処理を実行できます。

System_CAPS_noteメモ

EndInvoke を呼び出す場合、待機ハンドルは自動的に閉じられません。 待機ハンドルへのすべての参照を解放すると、ガベージ コレクションが待機ハンドルをクリアするときにシステム リソースが解放されます。 待機ハンドルの使用が終了すると同時にシステム リソースを解放するには、WaitHandle.Close メソッドを呼び出して破棄します。 破棄可能なオブジェクトが明示的に破棄されると、ガベージ コレクションはより効率的に動作します。

using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        static void Main() 
        {
            // The asynchronous method puts the thread id here.
            int threadId;

            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // Initiate the asychronous call.
            IAsyncResult result = caller.BeginInvoke(3000, 
                out threadId, null, null);

            Thread.Sleep(0);
            Console.WriteLine("Main thread {0} does some work.",
                Thread.CurrentThread.ManagedThreadId);

            // Wait for the WaitHandle to become signaled.
            result.AsyncWaitHandle.WaitOne();

            // Perform additional processing here.
            // Call EndInvoke to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, result);

            // Close the wait handle.
            result.AsyncWaitHandle.Close();

            Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".",
                threadId, returnValue);
        }
    }
}

/* This example produces output similar to the following:

Main thread 1 does some work.
Test method begins.
The call executed on thread 3, with return value "My call time was 3000.".
 */

IsCompleted によって返された IAsyncResultBeginInvoke プロパティを使用して、非同期呼び出しが完了したことを検出できます。 この方法は、ユーザー インターフェイスにサービスを提供するスレッドから非同期呼び出しを行う場合に使用します。 完了をポーリングすると、呼び出し元スレッドは、ThreadPool スレッドで非同期呼び出しを実行しながら、実行を継続できます。

using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        static void Main() {
            // The asynchronous method puts the thread id here.
            int threadId;

            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // Initiate the asychronous call.
            IAsyncResult result = caller.BeginInvoke(3000, 
                out threadId, null, null);

            // Poll while simulating work.
            while(result.IsCompleted == false) {
                Thread.Sleep(250);
                Console.Write(".");
            }

            // Call EndInvoke to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, result);

            Console.WriteLine("\nThe call executed on thread {0}, with return value \"{1}\".",
                threadId, returnValue);
        }
    }
}

/* This example produces output similar to the following:

Test method begins.
.............
The call executed on thread 3, with return value "My call time was 3000.".
 */

非同期呼び出しを開始したスレッドが結果を処理するスレッドである必要がない場合は、呼び出しが完了したときにコールバック メソッドを実行できます。 コールバック メソッドは ThreadPool スレッドで実行されます。

コールバック メソッドを使用するには、コールバック メソッドを表す BeginInvoke デリゲートを AsyncCallback に渡す必要があります。 コールバック メソッドで使用される情報を含むオブジェクトを渡すこともできます。 コールバック メソッドでは、唯一のパラメーターである IAsyncResultAsyncResult オブジェクトにキャストします。 こうすると、AsyncResult.AsyncDelegate プロパティを使用して、呼び出しを開始するために使用したデリゲートを取得し、EndInvoke を呼び出すことができるようになります。

例に関する注意事項

  • threadIdTestMethod パラメーターは out パラメーター (Visual Basic では <Out > ByRef) であるため、その入力値が TestMethod で使用されることはありません。 BeginInvoke 呼び出しにはダミー変数が渡されます。 threadId パラメーターが ref パラメーター (Visual Basic では ByRef) であった場合、BeginInvokeEndInvoke の両方に渡すことができるように、変数はクラス レベルのフィールドであることが必要です。

  • BeginInvoke に渡される状態情報は、コールバック メソッドが出力メッセージを書式指定するために使用する書式指定文字列です。Object として渡されるため、状態情報を使用するには適切な型にキャストする必要があります。

  • コールバックは ThreadPool スレッドで作成されます。 ThreadPool スレッドは、メイン スレッドが終了した場合はアプリケーションの実行を継続しないバックグラウンド スレッドであるため、例で使用するメイン スレッドは、コールバックが終了できるまでスリープする必要があります。

using System;
using System.Threading;
using System.Runtime.Remoting.Messaging;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        static void Main() 
        {
            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // The threadId parameter of TestMethod is an out parameter, so
            // its input value is never used by TestMethod. Therefore, a dummy
            // variable can be passed to the BeginInvoke call. If the threadId
            // parameter were a ref parameter, it would have to be a class-
            // level field so that it could be passed to both BeginInvoke and 
            // EndInvoke.
            int dummy = 0;

            // Initiate the asynchronous call, passing three seconds (3000 ms)
            // for the callDuration parameter of TestMethod; a dummy variable 
            // for the out parameter (threadId); the callback delegate; and
            // state information that can be retrieved by the callback method.
            // In this case, the state information is a string that can be used
            // to format a console message.
            IAsyncResult result = caller.BeginInvoke(3000,
                out dummy, 
                new AsyncCallback(CallbackMethod),
                "The call executed on thread {0}, with return value \"{1}\".");

            Console.WriteLine("The main thread {0} continues to execute...", 
                Thread.CurrentThread.ManagedThreadId);

            // The callback is made on a ThreadPool thread. ThreadPool threads
            // are background threads, which do not keep the application running
            // if the main thread ends. Comment out the next line to demonstrate
            // this.
            Thread.Sleep(4000);

            Console.WriteLine("The main thread ends.");
        }

        // The callback method must have the same signature as the
        // AsyncCallback delegate.
        static void CallbackMethod(IAsyncResult ar) 
        {
            // Retrieve the delegate.
            AsyncResult result = (AsyncResult) ar;
            AsyncMethodCaller caller = (AsyncMethodCaller) result.AsyncDelegate;

            // Retrieve the format string that was passed as state 
            // information.
            string formatString = (string) ar.AsyncState;

            // Define a variable to receive the value of the out parameter.
            // If the parameter were ref rather than out then it would have to
            // be a class-level field so it could also be passed to BeginInvoke.
            int threadId = 0;

            // Call EndInvoke to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, ar);

            // Use the format string to format the output message.
            Console.WriteLine(formatString, threadId, returnValue);
        }
    }
}

/* This example produces output similar to the following:

The main thread 1 continues to execute...
Test method begins.
The call executed on thread 3, with return value "My call time was 3000.".
The main thread ends.
 */
表示:
© 2016 Microsoft