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

War es hilfreich?

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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top