我正在研究在具有许多其事件订阅者的组件中进行异步事件调度的选项.在仔细阅读这些选项时,我遇到了这个例子:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
除了较旧的语法(示例来自.NET 1.1),我认为这是一个严重的资源泄漏.没有完成方法,没有轮询完成,或任何其他方式EndInvoke将被调用.
我的理解是每个人都BeginInvoke 必须有相应的EndInvoke.否则,有AsyncResult异常事件中引发的待处理对象实例以及异常事件期间引发的(可能)异常.
我意识到,通过提供回调和执行操作来改变它很容易EndInvoke,但如果我不需要...
处理异步延迟完全是另一回事,并且结合与UI线程同步的需要(即InvokeRequired等)可以很好地理解这些异步通知的整个想法.
那么,有两个问题:
BeginInvoke一个都需要相应的EndInvoke?调用BeginInvoke()应该与 a 配对EndInvoke(),但不这样做不会导致资源泄漏。返回IAsyncResult的BeginInvoke()将被垃圾收集。
这段代码中最大的陷阱是您很容易遇到终止应用程序的异常。您可能希望将委托调用包装在异常处理程序中,并考虑如何传播发生的异常(报告第一个异常、生成聚合异常等)。
使用调用删除BeginInvoke()将从线程队列中取出一个线程以开始运行该事件。这意味着该事件将始终触发主 UI 线程。这可能会使某些事件处理程序场景更难处理(例如更新 UI)。处理程序需要意识到它们需要调用主 UI 线程SynchronizationContext.Send()或.Post()与主 UI 线程同步。当然,所有其他多线程编程陷阱也适用。