ITコンサルの日常

ITコンサル会社に勤務する普通のITエンジニアの日常です。

続・デリゲートの非同期呼び出し

メソッドを非同期呼び出ししたものの、その後どこかで同期したい場合のやり方。

BeginInvokeの戻りとして、System.Runtime.Remoting.Messaging.AsyncResult型が戻されるので、こいつのメンバIsCompletedを使います。
AsyncResult.IsCompleted説は完全にウソでした。。
IsCompletedは

Gets a value indicating whether the server has completed the call.

ということで、呼び出しが終わった段階でtrueになってしまうので、コールバック関数の完了まで保証してくれません。というわけで、自前のIsCompletedを用意して、コールバック関数の終了時にtrueにするようにしました。これなら確実。
でも、WaitHandleとかいうのを使えばできそう?要追加調査だなこりゃ。
以下、単純化のため、マルチキャストじゃないデリゲートを使ったサンプル。

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

class FunctionPointer
{
        private static Boolean IsCompleted = false;

        public delegate int twoitem(int a, int b);

        public static int add(int a, int b)
        {
                return a + b;
        }

        private static void DisplayResult(IAsyncResult ar)
        {
                // キャストして元のデリゲートを取得する
                FunctionPointer.twoitem ptr = (FunctionPointer.twoitem)ar.AsyncS
tate;

                int result = ptr.EndInvoke(ar);
                Console.WriteLine("result = {0}", result);

                IsCompleted = true;
        }

        static void Main(string[] args)
        {
                // 関数ポインタを定義
                FunctionPointer.twoitem fp;

                fp = FunctionPointer.add;

                AsyncResult ar = (AsyncResult)fp.BeginInvoke(5, 3, new AsyncCall
back(DisplayResult), fp);

                Console.WriteLine("非同期呼び出し完了");

                while(!IsCompleted)
                {
                        Thread.Sleep(100);
                }
        }
}

結果はこう。

非同期呼び出し完了
result = 8

場合によっては、逆順に出ることもあり。

つまり、

Console.WriteLine("非同期呼び出し完了");

の行はデリゲートとは非同期に行われる(デリゲートの処理の完了に関わらず行われる)が、Mainメソッドの終了は、デリゲートと同期に行われるというわけです。