我在CodeProject上发现以下代码片段,异步调用方法... http://www.codeproject.com/Articles/14931/Asynchronous-Method-Invocation
private void CallFooWithOutAndRefParameters()
{
// create the paramets to pass to the function
string strParam1 = "Param1";
int intValue = 100;
ArrayList list = new ArrayList();
list.Add("Item1");
// create the delegate
DelegateWithOutAndRefParameters delFoo =
new DelegateWithOutAndRefParameters(FooWithOutAndRefParameters);
// call the beginInvoke function!
IAsyncResult tag =
delFoo.BeginInvoke(strParam1,
out intValue,
ref list,
null, null);
// normally control is returned right away,
// so you can do other work here...
// calling end invoke notice that intValue and list are passed
// as arguments because they might be updated within the function.
string strResult =
delFoo.EndInvoke(out intValue, ref list, tag);
// write down the parameters:
Trace.WriteLine("param1: " + strParam1);
Trace.WriteLine("param2: " + intValue);
Trace.WriteLine("ArrayList count: " + list.Count);
Trace.WriteLine("return value: " + strResult);
}
我对这段代码有几点不明白。
根据注释控件在遇到BeginInvoke行时立即返回到调用代码。
这是否意味着后面的代码(EndInvoke后跟一些跟踪日志记录)仅在FooWithOutAndRefParameters调用完成后自动运行(即使该代码驻留在同一方法中)。这对我来说有点混乱。 (我总是使用回调来做这种事。)
使用此方法我是否必须调用EndInvoke。我可以只是异步调用该方法而忘记它发生了吗?这有什么缺点吗?
如果我不调用EndInvoke(如此方法所示),那么我应该总是有回调吗?即使回调什么都不做。
如果答案你应该......然后你调用EndInvoke还是定义一个回调? (定义回调的好处是会通知您结果)
BTW我知道我可以在EndInvoke或回调中检查错误或记录结果(事实上我可能会这样做)。我想知道的是,有没有调用EndInvoke或定义回调(例如内存泄漏)的风险?什么是最佳做法。
赛斯
答案 0 :(得分:12)
是的,您必须调用EndInvoke()。不这样做会导致相当令人讨厌的资源泄漏,持续10分钟。底层管道是.NET Remoting,10分钟是,ahem,"默认终身租用时间"。经常这样做,你的程序将完成。让代表目标花费超过10分钟也会产生有趣的结果。
但最重要的是,如果不是调用操作的结果,则需要确定调用的方法是否成功完成。如果它在异常中死亡,那么在调用EndInvoke()之前,您将无法找到相关内容。此时重新引发异常。实际上处理该异常是相当棘手的,因为你不知道你的程序状态在爆炸之前被委托目标变异了多少。如果你真的想抓住目标,那么目标没有太多的副作用是非常重要的。或者相反,目标捕获并重新抛出异常,根据需要执行状态恢复。
您使用的示例当然是非常愚蠢的,很难在一个方法中的BeginInvoke和EndInvoke调用之间做任何有用的事情。您总是依赖于可以在BeginInvoke调用中注册的回调,倒数第二个参数。换句话说,不要像示例代码那样通过null
。并且喜欢BackgroundWorker和Task之类的类,甚至ThreadPool.QueueUserWorkItem()也可以从这段代码中解脱出来。使用委托的BeginInvoke()方法是非常低级别的黑客攻击。
代表是.NET中非常强大的抽象,但他们并没有很好地分发其功能。使用它们来实现事件是样板并且无故障。正确使用其BeginInvoke()方法是一种黑带艺术。一个值得注意的细节是它不再适用于.NETCore,从CoreCLR中删除了对远程处理的支持。
答案 1 :(得分:5)
在此之前,可能会对这些链接感兴趣:
Calling Synchronous Methods Asynchronously
Is Delegate.EndInvoke() really necessary?
现在,关于您的问题:
根据注释控件立即返回到调用代码 当它到达BeginInvoke行时。
是的,调用是异步调用的(我可以假设它使用另一个线程)。
这是否意味着后面的代码(EndInvoke后跟一些代码 跟踪日志记录)仅在FooWithOutAndRefParameters调用之后运行 自动完成...(即使代码存在于同一个代码中) 方法)。这对我来说有点混乱。 (我一直用 这种事情的回调。)
EndInvoke
将阻止执行,直到BeginInvoke启动的线程(方法)完成。在这种情况下,它类似于线程连接。
使用此方法我是否必须调用EndInvoke。我可以调用吗? 方法异常并忘记它发生了?这有什么缺点吗?
您必须始终致电EndInvoke
(见下文)。这可能有很多原因,但我认为最重要的原因是,如果方法失败,通过抛出异常,在调用EndInvoke之前,您将无法获得异常。
如果我不调用EndInvoke(如此方法所示),那么我应该这样做 总是有回调?即使回调什么都不做。
在这种情况下,回调必须从回调中调用EndInvoke
。所以只有回调是可选的。
如果答案你应该...然后你调用EndInvoke或定义一个 打回来? (定义回调的好处在于你 通知结果)
您不必定义回调,但如果这样做,则在其中调用EndInvoke。
由您来了解哪种情况更好:完全异步通知该方法已完成或强制使用该方法进行连接(从而阻止调用线程)。这完全取决于控制,你应该做一个或另一个,甚至两个。
BTW我知道我可以在EndInvoke中检查错误或记录结果 回调(我可能实际上是这样做的)。我想知道的是ARE 有没有调用EndInvoke或定义回调(内存)的风险 泄漏例如)?什么是最佳做法。
不是天生就没有,不,我不相信存在风险。但是,您必须始终检查方法是否失败或成功完成。
从MSDN开始,以下是BeginInvoke之后可以做的选项:
做一些工作然后调用EndInvoke来阻止直到通话 完成。
使用IAsyncResult.AsyncWaitHandle属性获取WaitHandle, 使用WaitOne方法阻止执行,直到WaitHandle为止 发信号,然后调用EndInvoke。
轮询BeginInvoke返回的IAsyncResult以确定何时 异步调用已完成,然后调用EndInvoke。
将一个回调方法的委托传递给BeginInvoke。方法是 异步调用完成时在ThreadPool线程上执行。 回调方法调用EndInvoke。
OBS:正如@ScottChamberlain在评论中所说,MSDN states that:
您可以调用
EndInvoke
从中检索返回值 代表,如果需要,但这不是必需的。 EndInvoke将阻止 直到可以检索返回值。
我认为背后的原因是,在处理Controls时,你是在UI线程上操作。由于EndInvoke会阻塞该线程,因此您可能有理由不想这样做。不过,我建议使用回调或轮询完成,以确保您的方法已成功完成。这会使您的程序更强大健壮(或错误恢复)。
答案 2 :(得分:1)
作为一种惯例,你应该调用EndInvoke,因为调用者可能让处理程序订阅了与你的处理无关的事件,但可能对使用者,以确保某些类型的处理可以在已知的时间/应用程序状态下进行。