异步操作中的异步操作
我的多线程知识仍然非常初级,所以非常感谢这里的一些指导。 我有一个接口 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
异步实现同步方法的最简单方法是将其放入委托中,并在生成的委托上使用
BeginInvoke
和EndInvoke
方法。 这将在线程池线程上运行同步方法,并且BeginInvoke
将返回IAsyncResult
实现,因此您不必实现它的核心。 但是,您确实需要将一些额外的数据偷偷放入IOperationInvoker.InvokeEnd
返回的IAsyncResult
中。 您可以通过创建一个IAsyncResult
的实现来轻松做到这一点,该实现将所有内容委托给内部IAsyncResult
,但有一个额外的字段来包含委托,以便当IAsyncResult
实例被传递给InvokeEnd
,您可以访问委托来调用EndInvoke
。但是,仔细阅读您的问题后,我发现您需要使用具有 COM 设置等的显式线程。
您需要做的是正确实现
IAsyncResult
。 几乎一切都由此而来,因为IAsyncResult
将包含同步所需的所有位。这是一个非常简单但效率不是很高的
IAsyncResult
实现。 它封装了所有基本功能:传递参数、同步事件、回调实现、从异步任务传播异常并返回结果。对于正确性来说,客户端代码无法看到
IAsyncResult
实现非常重要,否则它们可能会不恰当地访问SignalException
等方法或过早读取Result
。 如果没有必要,可以通过不构造WaitHandle
实现(示例中的ManualResetEvent
)来提高该类的效率,但这很难获得 100% 的正确。 此外,Thread
和ManualResetEvent
可以而且应该在End
实现中进行处理,就像对所有实现的对象所做的那样IDisposable
。 显然,End
应该检查以确保它已获得正确类的实现,以获得比强制转换异常更好的异常。 我省略了这些和其他细节,因为它们掩盖了异步实现的基本机制。The easiest way to implement a synchronous method asynchronously is to put it into a delegate, and use the
BeginInvoke
andEndInvoke
methods on the resulting delegate. This will run the synchronous method on a threadpool thread, andBeginInvoke
will return anIAsyncResult
implementation, so you don't have to implement the guts of it. However, you do need to smuggle a little extra data into theIAsyncResult
returned byIOperationInvoker.InvokeEnd
. You could do that easily by creating an implementation ofIAsyncResult
that delegates everything to an innerIAsyncResult
, but has an extra field to contain the delegate, so that when theIAsyncResult
instance is passed toInvokeEnd
, you can access the delegate to callEndInvoke
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 theIAsyncResult
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.It's important for correctness that client code can't see the
IAsyncResult
implementation, otherwise they might access methods likeSignalException
inappropriately or readResult
prematurely. The class can be made more efficient by not constructing theWaitHandle
implementation (ManualResetEvent
in the example) if it's not necessary, but this is tricky to get 100% right. Also, theThread
andManualResetEvent
can and should be disposed of in theEnd
implementation, as should be done with all objects that implementIDisposable
. 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.