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
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.