Domanda

La mia conoscenza multi-threading è ancora piuttosto rudimentale, quindi sarebbe davvero apprezzare alcune indicazioni qui. Ho un'interfaccia, IOperationInvoker (da WCF) che ha i seguenti metodi:

IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)

Data una realizzazione concreta di questa interfaccia, ho bisogno di implementare la stessa interfaccia, sollecitando nel contempo l'implementazione sottostante in un thread separato. (Nel caso in cui vi state chiedendo il motivo per cui, l'implmentation cemento chiama un oggetto COM eredità che deve essere in un diverso stato appartamento).

Al momento, sto facendo qualcosa di simile:

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);
    }
}

sto pensando che ho bisogno di concludere l'AsyncResult tornato con la mia, in modo da poter tornare al thread che abbiamo di spool fino ... ma onestamente io sono un po 'fuori dalla mia profondità. Eventuali puntatori?

Molte grazie,

James

È stato utile?

Soluzione

Il modo più semplice per implementare un metodo sincrono asincrono è di metterla in un delegato, ed utilizzare i metodi BeginInvoke e EndInvoke sul delegato risultante. Questo verrà eseguito il metodo sincrono su un thread ThreadPool, e BeginInvoke tornerà un'implementazione IAsyncResult, in modo da non dover implementare il coraggio di esso. Tuttavia, si ha bisogno di contrabbandare un po 'di dati aggiuntivi nel IAsyncResult restituito da IOperationInvoker.InvokeEnd. Si potrebbe fare così facilmente con la creazione di un'implementazione di IAsyncResult che i delegati di tutto per un IAsyncResult interna, ma ha un campo in più per contenere il delegato, in modo che quando l'istanza IAsyncResult è passata al InvokeEnd, è possibile accedere al delegato di chiamare EndInvoke su di esso .

Tuttavia, dopo una più stretta lettura della tua domanda, vedo che è necessario utilizzare un thread esplicito con le impostazioni COM ecc.

Quello che dovete fare è correttamente implementare IAsyncResult. Quasi tutto consegue, poiché il IAsyncResult conterrà tutti i bit necessari per la sincronizzazione.

Ecco una molto semplice, ma non terribilmente efficace, l'attuazione di IAsyncResult. Essa racchiude in sé tutte le caratteristiche essenziali:. Passaggio di argomenti, un evento di sincronizzazione, l'attuazione di callback, eccezioni che si propagano dal compito asincrona e il risultato tornando

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();
    }
}

E 'importante per la correttezza che il codice cliente non può vedere l'attuazione IAsyncResult, altrimenti potrebbero accedere metodi come SignalException in modo inappropriato o leggere Result prematuramente. La classe può essere reso più efficiente, non costruendo l'attuazione WaitHandle (ManualResetEvent nell'esempio) se non è necessario, ma questo è difficile da ottenere il 100% a destra. Inoltre, il Thread e ManualResetEvent possono e devono essere smaltiti in attuazione End, come dovrebbe essere fatto con tutti gli oggetti che implementano IDisposable. E ovviamente, End dovrebbe controllare per assicurarsi che ha ottenuto un'implementazione della classe diritto di ottenere una deroga più bello di un'eccezione cast. Ho lasciato questi e altri dettagli come si oscurano i meccanismi essenziali della realizzazione asincrona.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top