异步操作中的异步操作

时间:2009-04-18 13:10:51

标签: c# wcf multithreading apartments

我的多线程知识仍然非常简陋,所以非常感谢这里的一些指示。我有一个接口,IOperationInvoker(来自WCF),它有以下方法:

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

鉴于此接口的具体实现,我需要实现相同的接口,同时在单独的Thread中调用底层实现。 (如果你想知道为什么,具体的implmentation调用一个需要处于不同公寓状态的遗留COM对象。)

目前,我正在做这样的事情:

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

我认为我需要用自己的方式包装返回的AsyncResult,所以我可以回到我们已经假装的线程......但说实话,我有点超出我的深度。有什么指针吗?

非常感谢,

詹姆斯

1 个答案:

答案 0 :(得分:1)

异步实现同步方法的最简单方法是将其放入委托中,并在生成的委托上使用BeginInvokeEndInvoke方法。这将在线程池线程上运行同步方法,BeginInvoke将返回IAsyncResult实现,因此您不必实现它的内容。但是,您需要将一些额外数据走私到IAsyncResult返回的IOperationInvoker.InvokeEnd中。您可以通过创建IAsyncResult的实现来轻松地执行此操作,该实现将所有内容委派给内部IAsyncResult,但是有一个额外的字段来包含委托,以便在IAsyncResult实例传递给InvokeEnd,您可以访问该代表,在其上调用EndInvoke

然而,在仔细阅读您的问题之后,我发现您需要使用带有COM设置的显式线程等。

您需要做的是正确实施IAsyncResult。几乎所有事情都遵循这一点,因为IAsyncResult将包含同步所需的所有位。

这是IAsyncResult的一个非常简单但非常有效的实现。它封装了所有基本功能:传递参数,同步事件,回调实现,从异步任务传播异常并返回结果。

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

对于正确性而言,客户端代码无法看到IAsyncResult实现非常重要,否则他们可能会不恰当地访问SignalException等方法或过早地读取Result。如果没有必要,可以通过不构造WaitHandle实现(示例中为ManualResetEvent)来提高类的效率,但要实现100%正确这一点很棘手。此外,ThreadManualResetEvent可以并且应该在End实现中处理,与实现IDisposable的所有对象一样。显然,End应检查以确保它已获得正确类的实现以获得比强制转换异常更好的异常。我把这些和其他细节都遗漏了,因为它们模糊了异步实现的基本机制。