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:
我的多线程知识仍然非常简陋,所以非常感谢这里的一些指示。我有一个接口,IOperationInvoker(来自WCF),它有以下方法:
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).
给定此接口的具体实现,我需要实现相同的接口,同时在单独的Thread中调用底层实现。 (如果你想知道为什么,具体的implmentation调用一个需要处于不同公寓状态的传统COM对象)。
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?
我想我需要用我自己的方式包装返回的AsyncResult,所以我可以回到我们假装的线程......但说实话,我有点超出我的深度。有什么指针吗?
Many thanks,
James
1 个解决方案
#1
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.
异步实现同步方法的最简单方法是将其放入委托中,并在生成的委托上使用BeginInvoke和EndInvoke方法。这将在线程池线程上运行同步方法,而BeginInvoke将返回IAsyncResult实现,因此您不必实现它的内容。但是,您需要将一些额外的数据走私到IOperationInvoker.InvokeEnd返回的IAsyncResult中。你可以通过创建IAsyncResult的实现来轻松地做到这一点,IAsyncResult将所有内容委托给内部IAsyncResult,但是有一个额外的字段来包含委托,这样当IAsyncResult实例传递给InvokeEnd时,你可以访问委托来调用它上面的EndInvoke 。
However, after closer reading of your question, I see that you need to use an explicit thread with COM settings etc.
但是,在仔细阅读您的问题后,我发现您需要使用带有COM设置的显式线程等。
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.
您需要做的是正确实现IAsyncResult。几乎所有事情都是由此产生的,因为IAsyncResult将包含同步所需的所有位。
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.
这是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();
}
}
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.
对于正确性而言,客户端代码无法看到IAsyncResult实现非常重要,否则他们可能会不恰当地访问SignalException等方法或过早地读取Result。如果没有必要,可以通过不构造WaitHandle实现(示例中的ManualResetEvent)来提高类的效率,但要实现100%正确这一点很棘手。此外,Thread和ManualResetEvent可以并且应该在End实现中处理,应该对所有实现IDisposable的对象进行处理。显然,End应检查以确保它已获得正确类的实现以获得比强制转换异常更好的异常。我遗漏了这些和其他细节,因为它们模糊了异步实现的基本机制。
#1
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.
异步实现同步方法的最简单方法是将其放入委托中,并在生成的委托上使用BeginInvoke和EndInvoke方法。这将在线程池线程上运行同步方法,而BeginInvoke将返回IAsyncResult实现,因此您不必实现它的内容。但是,您需要将一些额外的数据走私到IOperationInvoker.InvokeEnd返回的IAsyncResult中。你可以通过创建IAsyncResult的实现来轻松地做到这一点,IAsyncResult将所有内容委托给内部IAsyncResult,但是有一个额外的字段来包含委托,这样当IAsyncResult实例传递给InvokeEnd时,你可以访问委托来调用它上面的EndInvoke 。
However, after closer reading of your question, I see that you need to use an explicit thread with COM settings etc.
但是,在仔细阅读您的问题后,我发现您需要使用带有COM设置的显式线程等。
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.
您需要做的是正确实现IAsyncResult。几乎所有事情都是由此产生的,因为IAsyncResult将包含同步所需的所有位。
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.
这是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();
}
}
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.
对于正确性而言,客户端代码无法看到IAsyncResult实现非常重要,否则他们可能会不恰当地访问SignalException等方法或过早地读取Result。如果没有必要,可以通过不构造WaitHandle实现(示例中的ManualResetEvent)来提高类的效率,但要实现100%正确这一点很棘手。此外,Thread和ManualResetEvent可以并且应该在End实现中处理,应该对所有实现IDisposable的对象进行处理。显然,End应检查以确保它已获得正确类的实现以获得比强制转换异常更好的异常。我遗漏了这些和其他细节,因为它们模糊了异步实现的基本机制。