异步操作中的异步操作

发布于 2024-07-17 05:42:13 字数 2411 浏览 4 评论 0原文

我的多线程知识仍然非常初级,所以非常感谢这里的一些指导。 我有一个接口 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 包装起来,这样我就可以回到我们已经假脱机的线程......但老实说,我是一个有点超出我的深度。 有什么指点吗?

非常感谢,

詹姆斯

My multi-threading knowledge is still pretty rudimentary, so would really appreciate some pointers here. I have an interface, IOperationInvoker (from WCF) which has the following methods:

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

Given a concrete implementation of this interface, I need to implement the same interface, whilst calling the underlying implementation in a seperate Thread. (in case you're wondering why, the concrete implmentation calls a legacy COM object which needs to be in a different apartment state).

At the moment, I'm doing something like this:

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

I'm thinking I need to wrap up the returned AsyncResult with my own, so I can get back to the thread we've spooled up... but honestly I'm a little out of my depth. Any pointers?

Many thanks,

James

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

維他命╮ 2024-07-24 05:42:13

异步实现同步方法的最简单方法是将其放入委托中,并在生成的委托上使用 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 应该检查以确保它已获得正确类的实现,以获得比强制转换异常更好的异常。 我省略了这些和其他细节,因为它们掩盖了异步实现的基本机制。

The easiest way to implement a synchronous method asynchronously is to put it into a delegate, and use the BeginInvoke and EndInvoke methods on the resulting delegate. This will run the synchronous method on a threadpool thread, and BeginInvoke will return an IAsyncResult implementation, so you don't have to implement the guts of it. However, you do need to smuggle a little extra data into the IAsyncResult returned by IOperationInvoker.InvokeEnd. You could do that easily by creating an implementation of IAsyncResult that delegates everything to an inner IAsyncResult, but has an extra field to contain the delegate, so that when the IAsyncResult instance is passed to InvokeEnd, you can access the delegate to call EndInvoke on it.

However, after closer reading of your question, I see that you need to use an explicit thread with COM settings etc.

What you need to do is properly implement IAsyncResult. Almost everything follows from this, since the IAsyncResult will contain all the bits needed for synchronization.

Here's a very simple, but not terribly efficient, implementation of IAsyncResult. It encapsulates all the essential features: passing arguments, a synchronization event, callback implementation, propagating exceptions from async task and returning result.

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

It's important for correctness that client code can't see the IAsyncResult implementation, otherwise they might access methods like SignalException inappropriately or read Result prematurely. The class can be made more efficient by not constructing the WaitHandle implementation (ManualResetEvent in the example) if it's not necessary, but this is tricky to get 100% right. Also, the Thread and ManualResetEvent can and should be disposed of in the End implementation, as should be done with all objects that implement IDisposable. And obviously, End should check to make sure that it has gotten an implementation of the right class to get a nicer exception than a cast exception. I've left these and other details out as they obscure the essential mechanics of the async implementation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文