いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

.NET クラスライブラリ探訪-017 (System.Runtime.Remoting.Messaging.AsyncResult) (非同期処理, Delegate, IAsyncResult)


Delegateを非同期で実行するには,

BeginInvoke
EndInvoke

メソッドを利用しますが、その際にBeginInvokeから取得できるのが
IAsyncResult型のオブジェクトになります。


このIAsyncResult型のオブジェクトですが、実態は以下のクラスのインスタンスです。

System.Runtime.Remoting.Messaging.AsyncResult

こちらのクラスにキャストすると、IAsyncResult型のままでは見えない以下のプロパティに
アクセスできます。

  • AsyncDelegate
    • BeginInvokeの呼び出し元のDelegateが取得できます。

このDelegateが取れると、わざわざ以下のようにしてBeginInvokeを呼ぶ必要がなくなります。

MethodInvoker invoker = new MethodInvoker( ( ) => { Thread.Sleep(TimeSpan.FromSeconds(3)); Console.WriteLine("hoge");});
invoker.BeginInvoke(CallbackMethod, invoker);

よく上記のようにして、AsyncStateに呼び出し元のDelegateを渡しているのを
ちょくちょく見ますが、それはコールバック側で呼び出し元のDelegateのEndInvokeを呼ぶ必要が
あるからです。で、IAsyncResult型では、そのプロパティがないので上記のように渡す必要があります。

void CallbackMethod(IAsyncResult ar){
    MethodInvoker invoker = ar.AsyncState as MethodInvoker;
    invoker.EndInvoker(ar);
}

AsyncResultにキャストすれば、直接取得できます。

void CallbackMethod(IAsyncResult ar){
    AsyncResult result = ar as AsyncResult;
    MethodInvoker invoker = result.AsyncDelegate as MethodInvoker;
    invoker.EndInvoke(result);
}


その他にも、AsyncResultには既にEndInvokeが呼ばれたかどうかを判定する

EndInvokeCalled

というプロパティもあります。


以下、サンプル.

#region AsyncResultSamples-01
    public class AsyncResultSamples01 : IExecutable{

        AutoResetEvent _are = new AutoResetEvent(false);

        public void Execute(){
            Func<DateTime, string> func = CallerMethod;

            IAsyncResult result = func.BeginInvoke(DateTime.Now, CallbackMethod, _are);
            _are.WaitOne();
        }

        string CallerMethod(DateTime d){
            return d.ToString("yyyy/MM/dd HH:mm:ss");
        }

        void CallbackMethod(IAsyncResult ar){
            AsyncResult            result = ar as AsyncResult;
            Func<DateTime, string> caller = result.AsyncDelegate as Func<DateTime, string>;
            EventWaitHandle        handle = result.AsyncState as EventWaitHandle;

            if(!result.EndInvokeCalled){
                Console.WriteLine(caller.EndInvoke(result));
                handle.Set();
            }
        }
    }
#endregion

上のサンプルで、わざわざIAsyncResult.AsyncWaitHandleを使っていないのは
AsyncWaitHandleの方は非同期処理自体(ここではCallerMethod)の処理が完了すると
シグナル状態となるのでそのままだとコールバックが呼ばれる前にメイン処理が終わってしまうからです。
AsyncWaitHandleはあくまで非同期処理自体に対して待機処理を行う際に利用します。
今回、サンプルなのでわざとコールバックが終了するまで待機してます。



================================
過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。