2009-04-18 7 views
2

Mein Multithreading-Wissen ist immer noch ziemlich rudimentär, daher würde ich hier einige Hinweise zu schätzen wissen. Ich habe eine Schnittstelle, IOperationInvoker (von WCF), die die folgenden Methoden hat:Asynchrone Operationen innerhalb einer asynchronen Operation

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, ruft die konkrete Implementierung ein veraltetes COM-Objekt auf, das sich in einem anderen Apartment-Zustand befinden 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 Thread wir haben wieder aufgepumpt ... aber ehrlich gesagt, bin ich etwas überfordert. Irgendwelche Zeiger?

Vielen Dank,

James

Antwort

1

Der einfachste Weg, eine synchrone Methode asynchron zu implementieren, ist es in einen Delegierten zu setzen, und verwenden Sie die BeginInvoke und EndInvoke Methoden auf dem resultierenden Delegierten. Dadurch wird die synchrone Methode für einen Threadpool-Thread ausgeführt, und BeginInvoke gibt eine IAsyncResult-Implementierung zurück, sodass Sie die Eingeweide davon nicht implementieren müssen. Sie müssen jedoch ein wenig zusätzliche Daten in die IAsyncResult, die von IOperationInvoker.InvokeEnd zurückgegeben wird, schmuggeln. Sie könnten dies leicht tun, indem Sie eine Implementierung von IAsyncResult erstellen, die alles an einen inneren IAsyncResult delegiert, aber ein zusätzliches Feld für den Delegaten enthält. Wenn die IAsyncResult Instanz an InvokeEnd übergeben wird, können Sie auf den Delegaten zugreifen, um EndInvoke darauf aufzurufen .

jedoch bei näherer Lektüre Ihrer Frage, ich sehe, dass Sie einen expliziten Thread mit COM-Einstellungen usw.

verwenden müssen, was Sie tun müssen, richtig IAsyncResult implementieren sind. Daraus ergibt sich fast alles, da die IAsyncResult alle für die Synchronisation benötigten Bits enthält.

Hier ist eine sehr einfache, aber nicht sehr effiziente Implementierung von IAsyncResult. Es kapselt alle wesentlichen Funktionen ein: Übergabe von Argumenten, Synchronisationsereignis, Callback-Implementierung, Weitergabe von Ausnahmen von der asynchronen Task und Rückgabe des Ergebnisses.

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 können, sonst könnten sie Methoden zugreifen wie SignalException oder unangemessen Result vorzeitig lesen. Die Klasse kann effizienter gemacht werden, indem die WaitHandle Implementierung (ManualResetEvent im Beispiel) nicht konstruiert wird, wenn es nicht notwendig ist, aber dies ist schwierig, um 100% richtig zu machen. Auch die Thread und ManualResetEvent können und sollten in der End Implementierung entsorgt werden, wie es bei allen Objekten der Fall sein sollte, die IDisposable implementieren. Und natürlich sollte End überprüfen, um sicherzustellen, dass es eine Implementierung der richtigen Klasse erhalten hat, um eine nettere Ausnahme als eine Cast-Ausnahme zu erhalten. Ich habe diese und andere Details weggelassen, da sie die wesentlichen Mechanismen der asynchronen Implementierung verschleiern.

+0

Vielen Dank Barry - ich werde das geben! –