3

これが、非同期プロセスを開始する拡張メソッドを作成する正しい方法であるかどうか、他のユーザーと再確認したいと思います。呼び出されたときに、そのプロセスを本質的に待機して結果を取得する関数を返します。

    public static Func<R> HandleInvoke<T, R>(this Func<T, R> function, T arg, Action<IAsyncResult> callback)
    {
        IAsyncResult result = function.BeginInvoke(arg, new AsyncCallback(callback), function);

        return delegate
        {
            return function.EndInvoke(result);
        };
    }

基本的には、次のように使用したい (疑似コード):

Func<R> myFunc = (some delegate).HandleInvoke(arg, callback);  
                     // at this point the operation begins, but will be nonblocking

                     // do other stuff

var result = myFunc();   // now I am deciding to wait on the result, which is blocking

この状況で WaitHandles を待つことを心配する必要があるかどうかはわかりませんでした。また、コールバックを渡す必要があるかどうかもわかりません。また、これは閉鎖を構成すると思いますか?

編集

これに尽きる、

    public static Func<R> HandleInvoke<T, R>(this Func<T, R> function, T arg)
    {
        IAsyncResult asyncResult = function.BeginInvoke(arg, iAsyncResult =>
            {
                if (!(iAsyncResult as AsyncResult).EndInvokeCalled)
                {
                    (iAsyncResult.AsyncState as Func<T, R>).EndInvoke(iAsyncResult);
                }

            }, function); 

        return delegate
        {
            WaitHandle.WaitAll(new WaitHandle[] { asyncResult.AsyncWaitHandle }); 

            return function.EndInvoke(asyncResult); 
        };
    }

これはうまくいくようです。コールバックは、EndInvoke が呼び出されたかどうかを確認し、呼び出されていない場合はそれを呼び出します。それ以外の場合は、返されたデリゲート内で EndInvoke が呼び出されます。

2回目の編集

これが私の最新の試みです-まだエラーをスローしておらず、うまく処理しているようです。デリゲートが function.EndInvoke() の結果を返した場所で機能させることができませんでしたが、匿名コールバックで EndInvoke が呼び出されるまでデリゲートは待機してから R を返します。また、各ケースで R が実際に割り当てられていることを確認するために、さらにチェックを使用することもできます。

    public static Func<R> HandleInvoke<T, R>(this Func<T, R> function, T arg)
    {
        R r = default(R); 

        IAsyncResult asyncResult = function.BeginInvoke(arg, result =>
            {
                r = (result.AsyncState as Func<T, R>).EndInvoke(result);

            }, function); 


        return delegate
        {
            while (!(asyncResult as AsyncResult).EndInvokeCalled)
            {
                Thread.Sleep(1); 
            }

            return r; 
        };
    }
4

2 に答える 2

2

これはうまくいくはずですが、私はデザインに熱心ではありません... ここに基本的な問題があります。

myFunc が呼び出された場合は、コールバックで EndInvoke を呼び出すべきではありませんが、myFunc が呼び出されていない場合は、戻り値を気にしないため、コールバックで EndInvoke を呼び出す必要があります。これにより、API の使用がわかりにくく、エラーが発生しやすくなります。

あなたがそこに持っている睡眠で、それは際どいですが、あなたを噛むことはあまりありません. これは、適切な同期プリミティブを使用して、すべてが正しい順序で行われることを保証します。これはテストされていないコードですが、動作するはずです

    public static Func<R> HandleInvoke<T, R>(this Func<T, R> function, T arg)
    {
        R retv = default(R);
        bool completed = false;

        object sync = new object();

        IAsyncResult asyncResult = function.BeginInvoke(arg, 
            iAsyncResult =>
            {
                lock(sync)
                {
                    completed = true;
                    retv = function.EndInvoke(iAsyncResult);
                    Monitor.Pulse(sync); // wake a waiting thread is there is one
                }
            }
            , null);

        return delegate
        {

            lock (sync)
            {
                if (!completed) // if not called before the callback completed
                {
                    Monitor.Wait(sync); // wait for it to pulse the sync object
                }
                return retv;
            }
        };
    }
于 2011-05-23T07:10:57.023 に答える
1

私はそれを行うためのより簡単な方法に出くわしました。そこでは、バックグラウンドスレッドではなく、呼び出しサイトで例外を(再)スローするという利点があります。

    public static Func<R> Future<T, R>(this Func<T, R> func, T arg)
    {
        IAsyncResult result = func.BeginInvoke(arg, null, null);

        return () => func.EndInvoke(result);
    }

アクションの場合もほぼ同じです。

    public static Action Future<T>(this Action<T> action, T arg)
    {
        IAsyncResult result = action.BeginInvoke(arg, null, null);

        return () => action.EndInvoke(result);
    }

ただし、結果が必要ない場合は、ThreadPool.QueueUserWorkItem()の方が効率的です。

于 2012-08-12T02:59:35.093 に答える