我正在研究在具有许多其事件订阅者的组件中进行异步事件调度的选项。在仔细阅读这些选项时,我遇到了这个例子:
public event ValueChangedEvent ValueChanged;
public void FireEventAsync(EventArgs e)
{
Delegate[] delegates = ValueChanged.GetInvocationList();
foreach (Delegate d in delegates)
{
ValueChangedEvent ev = (ValueChangedEvent)d;
ev.BeginInvoke(e, null, null);
}
}
除了较旧的语法(示例来自.NET 1.1)之外,我认为这是一个严重的资源泄漏。没有完成方法,没有轮询完成,或任何其他方式EndInvoke
将被调用。
我的理解是每个BeginInvoke
必须对应EndInvoke
。否则,会有异常AsyncResult
个对象实例,以及异步事件期间引发的(可能)异常。
我意识到通过提供回调并执行EndInvoke
来改变它很容易,但如果我不需要。 。
完全处理异步延迟是另一回事,并且结合需要与UI线程同步(即InvokeRequired
等),可以完全理解这些异步通知。
所以,有两个问题:
BeginInvoke
都需要相应的EndInvoke
?答案 0 :(得分:3)
对BeginInvoke()
的调用应与EndInvoke()
配对,但不执行此操作不会导致资源泄漏。 <{1}}返回的IAsyncResult
将被垃圾收集。
此代码中最大的缺陷是您很容易遇到终止应用程序的异常。您可能希望将委托调用包装在异常处理程序中,并考虑如何传播发生的异常(报告第一个,产生聚合异常等)。
使用BeginInvoke()
调用deletage将从线程队列中取出一个线程以开始运行该事件。这意味着事件将始终触发主UI线程。这可能使一些事件处理程序方案更难处理(例如,更新UI)。处理程序需要意识到需要调用BeginInvoke()
或SynchronizationContext.Send()
来与主UI线程同步。当然,所有其他多线程编程陷阱也适用。
答案 1 :(得分:1)
在考虑了一段时间之后,我得出的结论是,在Windows窗体控件中执行异步事件可能是一个坏主意。应在UI线程上引发Windows窗体事件。否则会给客户带来不必要的负担,并且可能会弄乱AsyncResult
个对象和异步异常。
让客户端触发自己的异步处理(使用BackgroundWorker
或其他技术),或者同步处理事件更清晰。
当然也有例外。例如,System.Timers.Timer
在线程池线程上引发Elapsed
事件。但是,最初的通知是在池线程上进行的。看起来一般规则是:在获得初始通知的同一线程上引发事件。至少,这是最适合我的规则。这样就不会有泄漏物体的问题。
答案 2 :(得分:0)