Question

Mes connaissances multi-threading est encore assez rudimentaire, donc apprécierais vraiment quelques conseils ici. Je une interface, IOperationInvoker (de WCF) qui a les méthodes suivantes:

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

Étant donné une mise en œuvre concrète de cette interface, je dois implémenter la même interface, tout en appelant la mise en œuvre sous-jacente dans une discussion séparée. (Au cas où vous vous demandez pourquoi, le implmentation concret appelle un héritage objet COM qui doit être dans un état différent de l'appartement).

En ce moment, je fais quelque chose comme ceci:

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

Je pense que je dois conclure le AsyncResult retourné avec mon propre, donc je peux revenir au fil que nous avons ... mais SPOOLED Honnêtement, je suis un peu hors de ma profondeur. Tous les pointeurs?

Merci,

James

Était-ce utile?

La solution

La meilleure façon de mettre en oeuvre un procédé synchrone est asynchrone pour le mettre dans un délégué, et en utilisant les méthodes de BeginInvoke et EndInvoke sur le délégué résultant. Cela exécutera la méthode synchrone sur un fil de threadpool et BeginInvoke retournera une implémentation IAsyncResult, de sorte que vous n'avez pas à mettre en œuvre le courage de celui-ci. Cependant, vous avez besoin de faire passer un peu de données supplémentaires dans le IAsyncResult retourné par IOperationInvoker.InvokeEnd. Vous pouvez le faire facilement en créant une mise en œuvre de IAsyncResult que les délégués tout à un IAsyncResult intérieur, mais a un champ supplémentaire pour contenir le délégué, de sorte que lorsque l'instance de IAsyncResult est passé à InvokeEnd, vous pouvez accéder au délégué à appeler EndInvoke sur elle .

Cependant, après lecture plus attentive de votre question, je vois que vous avez besoin d'utiliser un fil explicite avec les paramètres COM etc.

Ce que vous devez faire est de mettre en œuvre correctement IAsyncResult. Presque tout découle de cela, puisque le IAsyncResult contiendra tous les bits nécessaires pour la synchronisation.

Voici un très simple, mais pas très efficace, la mise en œuvre de IAsyncResult. Il englobe toutes les caractéristiques essentielles:. Arguments en passant, un événement de synchronisation, la mise en œuvre de rappel, des exceptions de tâche se propageant async et le résultat retour

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

Il est important pour l'exactitude que le code client ne peut pas voir la mise en œuvre de IAsyncResult, sinon ils pourraient accéder à des méthodes comme SignalException inappropriée ou lire Result prématurément. La classe peut être de ne pas construire la mise en œuvre de WaitHandle rendu plus efficace (ManualResetEvent dans l'exemple) si elle n'est pas nécessaire, mais cela est difficile à obtenir 100%. En outre, le Thread et ManualResetEvent peuvent et doivent être éliminés dans la mise en œuvre de End, comme cela devrait être fait avec tous les objets qui mettent en œuvre IDisposable. Et de toute évidence, End doit vérifier pour vous assurer qu'il a obtenu une mise en œuvre de la bonne classe pour obtenir une plus belle exception qu'une exception coulée. Je l'ai laissé ces derniers et d'autres détails comme ils obscurcissent les mécanismes essentiels de la mise en œuvre async.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top