非同期操作中の非同期操作
-
11-09-2019 - |
質問
私のマルチスレッドの知識はまだかなり初歩的であるので、ここでいくつかのポインタを本当にいただければ幸いです。私は、次のメソッドを有するインターフェース、(WCF)からIOperationInvokerを有する
IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
このインタフェースの具体的な実装を考えると、私は別々のスレッドで基礎となる実装を呼び出しながら、同じインターフェイスを実装する必要があります。 (あなたはなぜ迷っている場合には、具体的なimplmentationは異なるアパートメント状態にする必要が従来のCOMオブジェクトを呼び出します)。
現時点では、私はこのような何かをやってます:
public StaOperationSyncInvoker : IOperationInvoker {
IOperationInvoker _innerInvoker;
public StaOperationSyncInvoker(IOperationInvoker invoker) {
this._innerInvoker = invoker;
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
Thread t = new Thread(BeginInvokeDelegate);
InvokeDelegateArgs ida = new InvokeDelegateArgs(_innerInvoker, instance, inputs, callback, state);
t.SetApartmentState(ApartmentState.STA);
t.Start(ida);
// would do t.Join() if doing syncronously
// how to wait to get IAsyncResult?
return ida.AsyncResult;
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
// how to call invoke end on the
// thread? could we have wrapped IAsyncResult
// to get a reference here?
return null;
}
private class InvokeDelegateArgs {
public InvokeDelegateArgs(IOperationInvoker invoker, object instance, object[] inputs, AsyncCallback callback, object state)
{
this.Invoker = invoker;
this.Instance = instance;
this.Inputs = inputs;
this.Callback = callback;
this.State = state;
}
public IOperationInvoker Invoker { get; private set; }
public object Instance { get; private set; }
public AsyncCallback Callback { get; private set; }
public IAsyncResult AsyncResult { get; set; }
public Object[] Inputs { get; private set; }
public Object State { get; private set; }
}
private static void BeginInvokeDelegate(object data)
{
InvokeDelegateArgs ida = (InvokeDelegateArgs)data;
ida.AsyncResult = ida.Invoker.InvokeBegin(ida.Instance, ida.Inputs, ida.Callback, ida.State);
}
}
私は、私は自分で返さAsyncResultをラップする必要があると思っていますので、私は戻って私たちがアップスプールきたスレッドに取得することができます...しかし、正直なところ、私は私の深さのうち少しです。任意のポインタ?
多くのおかげで、
ジェームズ
解決
非同期同期メソッドを実装する最も簡単な方法は、デリゲートに入れ、そして得られたデリゲートにBeginInvoke
とEndInvoke
メソッドを使用することです。これは、スレッドプールのスレッドで同期メソッドを実行すると、あなたはそれの根性を実装する必要はありませんのでBeginInvoke
は、IAsyncResult
の実装を返します。ただし、IAsyncResult
によって返さIOperationInvoker.InvokeEnd
に少し余分なデータを密輸する必要があります。あなたは、内側IAsyncResult
への委任すべてが、しかしIAsyncResult
インスタンスがIAsyncResult
に渡されたとき、あなたはそれにInvokeEnd
を呼び出すためのデリゲートにアクセスできるように、デリゲートを含むように余分なフィールドを持っているEndInvoke
の実装を作成することにより、簡単にそれを行うことができます。
しかし、あなたの質問の近くに読んだ後、私はあなたがCOMの設定などで明示的にスレッドを使用する必要があることがわかります。
何をする必要があるIAsyncResult
を実装して、適切です。 IAsyncResult
は、同期のために必要なすべてのビットが含まれていますので、ほとんどすべてが、このことから、次の。
ここでIAsyncResult
の非常にシンプルですが、ひどく効率的ではない、実装があります。これは、すべての本質的な特徴をカプセル化:渡す引数、同期イベント、コールバックの実装、非同期タスクと返す結果から例外を伝播する
using System;
using System.Threading;
class MyAsyncResult : IAsyncResult
{
object _state;
object _lock = new object();
ManualResetEvent _doneEvent = new ManualResetEvent(false);
AsyncCallback _callback;
Exception _ex;
bool _done;
int _result;
int _x;
public MyAsyncResult(int x, AsyncCallback callback, object state)
{
_callback = callback;
_state = state;
_x = x; // arbitrary argument(s)
}
public int X { get { return _x; } }
public void SignalDone(int result)
{
lock (_lock)
{
_result = result;
_done = true;
_doneEvent.Set();
}
// never invoke any delegate while holding a lock
if (_callback != null)
_callback(this);
}
public void SignalException(Exception ex)
{
lock (_lock)
{
_ex = ex;
_done = true;
_doneEvent.Set();
}
if (_callback != null)
_callback(this);
}
public object AsyncState
{
get { return _state; }
}
public WaitHandle AsyncWaitHandle
{
get { return _doneEvent; }
}
public bool CompletedSynchronously
{
get { return false; }
}
public int Result
{
// lock (or volatile, complex to explain) needed
// for memory model problems.
get
{
lock (_lock)
{
if (_ex != null)
throw _ex;
return _result;
}
}
}
public bool IsCompleted
{
get { lock (_lock) return _done; }
}
}
class Program
{
static void MyTask(object param)
{
MyAsyncResult ar = (MyAsyncResult) param;
try
{
int x = ar.X;
Thread.Sleep(1000); // simulate lengthy work
ar.SignalDone(x * 2); // demo work = double X
}
catch (Exception ex)
{
ar.SignalException(ex);
}
}
static IAsyncResult Begin(int x, AsyncCallback callback, object state)
{
Thread th = new Thread(MyTask);
MyAsyncResult ar = new MyAsyncResult(x, callback, state);
th.Start(ar);
return ar;
}
static int End(IAsyncResult ar)
{
MyAsyncResult mar = (MyAsyncResult) ar;
mar.AsyncWaitHandle.WaitOne();
return mar.Result; // will throw exception if one
// occurred in background task
}
static void Main(string[] args)
{
// demo calling code
// we don't need state or callback for demo
IAsyncResult ar = Begin(42, null, null);
int result = End(ar);
Console.WriteLine(result);
Console.ReadLine();
}
}
これは、そうでない場合は、不適切IAsyncResult
のようなメソッドにアクセスしたり、途中でSignalException
を読んで、クライアントコードがResult
の実装を見ることができない正当性が重要です。このクラスは、それが必要ではない場合WaitHandle
の実装(例ではManualResetEvent
)を構築しないことによって、より効率的にすることができるが、これは100%の権利を取得するのが難しいです。 Thread
を実装するすべてのオブジェクトで行われるべきであるとしても、ManualResetEvent
とEnd
はと、IDisposable
実装で処分しなければならないことができます。そして、明らかに、End
は、それがキャスト例外よりもよりよい例外を取得する権利クラスの実装を得ていることを確認するためにチェックする必要があります。彼らは非同期実装の基本的な仕組みを分かりにくくように私は、これらおよびその他の詳細を残してきています。