Pergunta

O meu conhecimento de multi-threading ainda é bastante rudimentar, então realmente aprecio algumas dicas aqui. Eu tenho uma interface, IOperationInvoker (de WCF) que tem os seguintes métodos:

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

Dada uma implementação concreta desta interface, Eu preciso implementar a mesma interface, enquanto que chamar a implementação subjacente em um segmento separado. (Caso você esteja se perguntando por que, o implmentation concreto chama um objeto COM legado que precisa estar em um estado apartamento diferente).

No momento, eu estou fazendo algo parecido com isto:

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

Eu estou pensando que eu preciso para encerrar a AsyncResult voltou com a minha, para que eu possa voltar para o segmento temos spool-se ... mas honestamente, eu estou um pouco fora de minha profundidade. Os ponteiros?

Muito obrigado,

James

Foi útil?

Solução

A maneira mais fácil de implementar um método síncrono de forma assíncrona é colocá-lo em um delegado, e usar os métodos BeginInvoke e EndInvoke na delegado resultante. Isto irá executar o método síncrono em um segmento pool de threads, e BeginInvoke irá retornar uma implementação IAsyncResult, para que você não tem que implementar a coragem dele. No entanto, você precisa fazer para contrabandear um pouco de dados extra para o IAsyncResult retornado por IOperationInvoker.InvokeEnd. Você poderia fazer isso facilmente através da criação de uma implementação de IAsyncResult que os delegados tudo para um IAsyncResult interior, mas tem um campo extra para conter o delegado, de modo que quando a instância IAsyncResult é passado para InvokeEnd, você pode acessar o delegado para EndInvoke chamada nele .

No entanto, após mais perto leitura da sua pergunta, eu vejo que você precisa usar um fio explícita com COM configurações etc.

O que você precisa fazer é implementar adequadamente IAsyncResult. Quase tudo decorre, desde o IAsyncResult irá conter todos os bits necessários para a sincronização.

Aqui está uma forma muito simples, mas não muito eficiente, implementação de IAsyncResult. Ele incorpora todas as características essenciais:. Argumentos passagem, um evento de sincronização, a implementação de retorno de chamada, propagando excepções tarefa assíncrona e resultado retornando

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

É importante para a correção que o código do cliente não pode ver a implementação IAsyncResult, caso contrário, eles podem acessar métodos como SignalException inadequada ou ler Result prematuramente. A classe pode ser mais eficiente por não construir a implementação WaitHandle (ManualResetEvent no exemplo) se não é necessário, mas isso é complicado para obter 100% certo. Além disso, o Thread e ManualResetEvent podem e devem ser eliminados na implementação End, como deve ser feito com todos os objetos que implementam IDisposable. E, obviamente, End deve verificar para se certificar de que tem obtido uma implementação da classe direito de obter uma exceção mais agradável do que uma exceção de elenco. Eu deixei estes e outros detalhes como eles obscurecer os mecanismos essenciais da implementação assíncrona.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top