Asynchrone Operationen innerhalb eines asynchronen Betrieb
-
11-09-2019 - |
Frage
Mein Multi-Threading Wissen ist immer noch ziemlich rudimentär, so wirklich einige Hinweise hier schätzen würde. Ich habe eine Schnittstelle, IOperationInvoker (von WCF), die die folgenden Methoden hat:
IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
eine konkrete Implementierung dieser Schnittstelle gegeben, ich brauche die gleiche Schnittstelle zu implementieren, während die zugrunde liegende Implementierung in einem separaten Thread aufrufen. (Falls Sie sich fragen, warum die konkrete implmentation ein Vermächtnis COM-Objekt aufruft, die in einer anderen Wohnung Zustand sein muss).
Im Moment bin ich so etwas wie dies zu tun:
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);
}
}
Ich denke, ich das zurück AsyncResult mit meinem eigenen einpacken müssen, so kann ich auf den Faden wieder wir aufgespult haben ... aber ehrlich gesagt bin ich ein wenig aus meiner Tiefe. Alle Zeiger?
Vielen Dank,
James
Lösung
Der einfachste Weg, um eine synchrone Methode asynchron zu implementieren, ist es in einen Delegierten zu setzen, und die BeginInvoke
und EndInvoke
Methoden auf der resultierende Delegat verwenden. Dadurch wird die synchrone Methode auf einem Threadpool-Thread ausgeführt werden, und BeginInvoke
wird eine IAsyncResult
Implementierung zurückkehren, so dass Sie den Mut, es nicht implementieren müssen. Aber Sie brauchen ein wenig mehr Daten in die IAsyncResult
von IOperationInvoker.InvokeEnd
zurück zu schmuggeln. Könnte man so leicht tun, indem eine Implementierung von IAsyncResult
schaffen, die Delegierten alles zu einem inneren IAsyncResult
, sondern ein zusätzliches Feld der Delegierten zu enthalten hat, so dass, wenn die IAsyncResult
Instanz InvokeEnd
geben wird, Sie die Delegaten EndInvoke
zugreifen kann darauf anrufen .
Doch nach genauer Lektüre Ihrer Frage, ich sehe, dass Sie einen expliziten Thread mit COM-Einstellungen usw. verwenden müssen.
Was Sie tun müssen, ist richtig IAsyncResult
implementieren. Fast folgt alles von diesem, da die IAsyncResult
alle Bits für die Synchronisation benötigt enthalten wird.
Hier ist eine sehr einfache, aber nicht sehr effizient, die Umsetzung von IAsyncResult
. Sie kapselt alle wesentlichen Merkmale:. Geben Argumente, ein Synchronisationsereignis, Callback-Implementierung, propagieren Ausnahmen von Asynchron-Aufgabe und Rückkehr Ergebnis
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();
}
}
Es ist wichtig, für die Richtigkeit, dass Client-Code nicht die IAsyncResult
Implementierung sehen kann, sonst könnten sie Methoden wie SignalException
Zugriff unpassend oder Result
vorzeitig lesen. Die Klasse kann nicht durch den Aufbau der WaitHandle
Implementierung (ManualResetEvent
im Beispiel) effizienter gemacht werden, wenn es nicht notwendig ist, aber dies ist schwierig zu 100% richtig zu machen. Auch können die Thread
und ManualResetEvent
und sollen in der End
Implementierung angeordnet sein, wie sollten mit allen Objekten durchgeführt werden, die IDisposable
implementieren. Und natürlich sollte, End
zu überprüfen, um sicherzustellen, dass es eine Implementierung der richtigen Klasse bekommen hat ein schöneres Ausnahme als eine Besetzung Ausnahme zu erhalten. Ich habe diese und andere Details weggelassen, da sie die wesentlichen Mechanik der Asynchron-Implementierung verschleiern.