【问题标题】:Asynchronous operations within an asynchronous operation异步操作中的异步操作
【发布时间】:2023-03-16 10:44:01
【问题描述】:

我的多线程知识还很初级,所以非常感谢这里的一些指点。我有一个接口 IOperationInvoker(来自 WCF),它具有以下方法:

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

鉴于此接口的具体实现,我需要实现相同的接口,同时在单独的线程中调用底层实现。 (如果您想知道为什么,具体实现会调用需要处于不同公寓状态的遗留 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,这样我才能回到我们已经假脱机的线程......但老实说,我有点超出我的深度。有什么指点吗?

非常感谢,

詹姆斯

【问题讨论】:

    标签: c# wcf multithreading apartments


    【解决方案1】:

    异步实现同步方法的最简单方法是将其放入委托中,并在生成的委托上使用BeginInvokeEndInvoke 方法。这将在线程池线程上运行同步方法,BeginInvoke 将返回一个IAsyncResult 实现,因此您不必实现它的胆量。但是,您确实需要将一些额外的数据偷运到IOperationInvoker.InvokeEnd 返回的IAsyncResult 中。您可以通过创建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 应该检查以确保它已经实现了正确的类,以获得比强制转换异常更好的异常。我省略了这些和其他细节,因为它们模糊了异步实现的基本机制。

    【讨论】:

    • 非常感谢巴里 - 我会试试看的!
    猜你喜欢
    • 1970-01-01
    • 2020-10-25
    • 2023-03-31
    • 2017-06-29
    • 2018-10-30
    • 1970-01-01
    • 1970-01-01
    • 2016-02-23
    • 2016-06-02
    相关资源
    最近更新 更多