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でのみブレークできます。
わかりやすくするために、このサンプルコードを紹介します。新しいコンソールアプリを作成して使用してください
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オブジェクトを取得します。このIAsynchResultオブジェクトは、AyncStateによって、BeginInvokeメソッド呼び出しで渡した元のオブジェクトが提供されます。この場合(および一般的な方法として)、デリゲート自体をオブジェクト( "関数"と呼ばれる変数)として渡します。コールバックが呼び出された後、ar.AsyncStateをクエリして元のデリゲートオブジェクトを取得し、そのオブジェクトに対してEndInvokeを呼び出して結果を返しました。
ブレークポイントがヒットしないので、それについてさらに情報が必要だと思います。どういう意味ですか?このConsole.WriteLineステートメントはどこにありますか?
新しい応答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);
}
この記事 何が起こっているのかを理解するのに役立ちました。 WcfのOperationContract
は特別な非同期パターンを実装し、別のスレッドで[操作]を同期的に呼び出します。 Begin [Operation]とEnd [Operation]はパターンの作成に使用されますが、実際には呼び出されません。したがって、このパターンとそのシグネチャと属性は、クライアントで同期呼び出しを行う場合と同じように見えます。 BackgroundWorker。
BeginOperation互換のシグネチャを持つメソッドでは、AsyncPattern [of OperationContract
attribute]をtrueにのみ設定でき、定義するコントラクトには、EndOperation互換のシグネチャを持つ一致するメソッドも必要です。これらの要件は、プロキシのロード時に確認されます。 AsyncPatternは、基になる同期メソッドをBegin/Endペアにバインドし、同期実行と非同期実行を関連付けます。簡単に言うと、クライアントがAsyncPatternをtrueに設定してBeginOperation形式のメソッドを呼び出すと、サービス上のその名前でメソッドを直接呼び出さないようにWCFに指示します。代わりに、スレッドプールのスレッドを使用して、基になるメソッド(アクション名で識別)を同期的に呼び出します。同期呼び出しは、呼び出し元のクライアントではなく、スレッドプールからのスレッドをブロックします。クライアントがブロックされるのは、呼び出し要求をスレッドプールにディスパッチするのにかかる少しの間だけです。同期呼び出しの返信メソッドは、EndOperationメソッドと関連付けられています。