16

AsyncCallbackとIAsyncResultを使用したコールバックパターンに関する2つの質問。

コード例で質問を変更しました:

using System;
using System.Collections.Generic;
using System.Text;

namespace TestAsync
{
    class Program
    {
        private static Wrapper test = new Wrapper();

        static void Main(string[] args)
        {
            test.BeginMethod("parameter 1", "parameter 2", Callback);
            Console.ReadKey();
        }

        private static void Callback(IAsyncResult ar)
        {
            string result = test.EndMethod(ar);
        }
    }

    public interface ITest
    {
        IAsyncResult BeginMethod(string s1, string s2, AsyncCallback cb, object state);
        string EndMethod(IAsyncResult result);
    }

    public class Wrapper
    {
        private ITest proxy = new Test();

        public void BeginMethod(string s1, string s2, AsyncCallback cb)
        {
            proxy.BeginMethod(s1, s2, cb, proxy);
        }

        public string EndMethod(IAsyncResult result)
        {
            return ((ITest)(result.AsyncState)).EndMethod(result);
        }
    }

    public class Test : ITest
    {
        private string WorkerFunction(string a, string b)
        {
            // "long running work"
            return a + "|" + b;
        }

        public IAsyncResult BeginMethod(string s1, string s2, AsyncCallback cb, object state)
        {
            Func<string, string, string> function = new Func<string, string, string>(WorkerFunction);
            IAsyncResult result = function.BeginInvoke(s1, s2, cb, state);
            return result;
        }

        public string EndMethod(IAsyncResult result)
        {
            return (string)(result.AsyncState);
        }
    }

    public delegate TResult Func<T1, T2, TResult>(T1 t1, T2 t2);
}

編集
を開始私は何が起こっているのかを見始めています。WCF非同期パターンと通常の非同期パターンを混同しました。WCFでは、プロキシを使用し、Begin-およびEndMethodには、関数デリゲートではなくプロキシを渡す必要があります。WCFの場合、キャストは機能しますが、通常の場合は機能しません。WCFは、[OperationContract(AsyncPattern = true)]属性を使用して、おそらく多少異なるパターンを適用します。編集終了

なぜ回線にエラーがあるのreturn (string)(result.AsyncState);ですか?
本番コードとまったく同じパターンで問題ありません。

次に、クラスTestのBeginMethodでコードをデバッグできないのはなぜですか?
私はWorkerFunctionでしか中断できません。

4

2 に答える 2

28

わかりやすくするために、このサンプル コードを示します。新しいコンソール アプリを作成し、これを使用してください

public class Test
{
    private int WorkerFunction(string a, string b)
    {
        //this is the guy that is supposed to do the long running work 
        Console.WriteLine(a);
        Console.WriteLine(b);
        return a.Length + b.Length;
    }

    private void MyCallBack(IAsyncResult ar)
    {
        Func<string, string, int> function = ar.AsyncState as Func<string, string, int>;
        int result = function.EndInvoke(ar);
        Console.WriteLine("Result is {0}", result);
    }
    public void CallMethod()
    {
        Func<string, string, int> function = new Func<string, string, int>(WorkerFunction);
        IAsyncResult result = function.BeginInvoke("param1", "param2", MyCallBack, function);
    }


}

class Program
{

    static void Main(string[] args)
    {
        Test test = new Test();
        test.CallMethod();
    }
}

ご覧のとおり、コールバック関数 (MyCallBack) は、返された IAsyncResult オブジェクトを取得します。AyncState が BeginInvoke メソッド呼び出しで渡した元のオブジェクトを提供するのは、この IAsynchResult オブジェクトです。この場合 (そして一般的な方法として)、デリゲート自体をオブジェクト (「関数」と呼ばれる変数) として渡します。コールバックが呼び出されたら、ar.AsyncState をクエリして元のデリゲート オブジェクトを取得し、EndInvoke を呼び出して結果を取得しました。

ブレークポイントがヒットしていないことについては、もう少し情報が必要です。正確にはどういう意味ですか?この Console.WriteLine ステートメントはどこにありますか?

NEW RESPONSE OK ここにあなたのコードの私のバージョンがあります。基本的にどこから EndInvoke を呼び出しても、実際のデリゲート オブジェクトで呼び出す必要があります (この場合、インスタンス化する「関数」変数で、実際の IAsyncResult オブジェクトを渡します)。あなたが持っているコードはこの機能を隠蔽しようとしていますが、これを行うにはもっと複雑な方法があると言わざるを得ません。ご希望があれば、喜んでラッパーを作成します。今のところ、私はあなたのコードに私の小さな追加を加えてあなたに返すだけです。それはそれが機能するはずです. クラスレベルの変数を使用しているため、自分で使用する必要があります。現時点では、これは実際にはスレッドセーフではありません。しかし、ここに行きます

using System;
using System.Collections.Generic;
using System.Text;

namespace TestAsync
{
    class Program
    {
        private static Wrapper test = new Wrapper();

        static void Main(string[] args)
        {
            var objectState = new object();
            test.BeginMethod("parameter 1", "parameter 2", Callback, objectState);
            Console.ReadKey();
        }

        private static void Callback(IAsyncResult ar)
        {
            string result = test.EndMethod(ar);
            Console.WriteLine(result);
        }
    }

    public interface ITest
    {
        IAsyncResult BeginMethod(string s1, string s2, AsyncCallback cb, object state);
        string EndMethod(IAsyncResult result);
    }

    public class Wrapper
    {
        private ITest proxy = new Test();

        public void BeginMethod(string s1, string s2, AsyncCallback cb)
        {
            proxy.BeginMethod(s1, s2, cb, proxy);
        }

        public string EndMethod(IAsyncResult result)
        {
            return ((ITest)(result.AsyncState)).EndMethod(result);
        }
    }

    public class Test : ITest
    {
        Func<string, string, string> _delgateObject;
        private string WorkerFunction(string a, string b)
        {
            // "long running work"
            return a + "|" + b;
        }

        public IAsyncResult BeginMethod(string s1, string s2, AsyncCallback cb, object state)
        {
            Func<string, string, string> function = new Func<string, string, string>(WorkerFunction);
            this._delgateObject = function;
            IAsyncResult result = function.BeginInvoke(s1, s2, cb, state);
            return result;
        }

        public string EndMethod(IAsyncResult result)
        {
            var test = result.AsyncState;
            return this._delgateObject.EndInvoke(result);
        }
    }

    public delegate TResult Func<T1, T2, TResult>(T1 t1, T2 t2);
}
于 2011-02-23T11:12:20.210 に答える
2

この記事は、何が起こっているのかを理解するのに役立ちました。Wcf は、別のOperationContractスレッドで [Operation] を同期的に呼び出す特別な Async パターンを実装しています。Begin[Operation] と End[Operation] はパターンを作成するために使用されますが、実際には呼び出されません。そのため、署名と属性を含むこのパターンは、BackgroundWorker などを介してクライアントで同期呼び出しを行う場合と同じように見えます。

AsyncPattern [of] のみを設定できますOperationContract属性] を BeginOperation 互換の署名を持つメソッドで true に設定し、定義するコントラクトにも EndOperation 互換の署名を持つ一致するメソッドが必要です。これらの要件は、プロキシのロード時に検証されます。AsyncPattern が行うことは、基礎となる同期メソッドを Begin/End ペアにバインドし、同期実行を非同期実行と関連付けることです。簡単に言うと、クライアントが AsyncPattern を true に設定して BeginOperation の形式のメソッドを呼び出すと、サービスでその名前のメソッドを直接呼び出そうとしないように WCF に指示します。代わりに、スレッド プールのスレッドを使用して、基になるメソッド (アクション名で識別) を同期的に呼び出します。同期呼び出しは、呼び出し元のクライアントではなく、スレッド プールからスレッドをブロックします。クライアントは、呼び出し要求をスレッド プールにディスパッチするのにかかるごくわずかな時間だけブロックされます。同期呼び出しの応答メソッドは、EndOperation メソッドと関連付けられています。

于 2011-02-23T22:33:31.330 に答える