C# 异步引发事件
我正在研究在一个组件中执行异步事件调度的选项,该组件有许多事件订阅服务器。在仔细阅读这些选项时,我遇到了以下示例:C# 异步引发事件,c#,.net,winforms,C#,.net,Winforms,我正在研究在一个组件中执行异步事件调度的选项,该组件有许多事件订阅服务器。在仔细阅读这些选项时,我遇到了以下示例: public event ValueChangedEvent ValueChanged; public void FireEventAsync(EventArgs e) { Delegate[] delegates = ValueChanged.GetInvocationList(); foreach (Delegate d in delegates) {
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);
}
}
除了较旧的语法(示例来自.NET1.1),在我看来这是一个严重的资源泄漏。没有完成方法,没有完成轮询,也没有调用EndInvoke
的任何其他方式
我的理解是,每个BeginInvoke
都必须有相应的EndInvoke
。否则,将有挂起的AsyncResult
对象实例浮动,以及(可能)在异步事件期间引发的异常
我意识到,通过提供回调并执行EndInvoke
,很容易改变这一点,但如果我不需要
处理异步实例完全是另一回事,再加上需要与UI线程同步(即,invokererequired
,等等),可以很好地支持执行这些异步通知的整个想法
因此,有两个问题:
BeginInvoke
都需要一个相应的EndInvoke
,对吗对
BeginInvoke()
的调用应与EndInvoke()
配对,但不这样做不会导致资源泄漏。BeginInvoke()
返回的IAsyncResult
将被垃圾收集
这段代码中最大的陷阱是,在终止应用程序时,您很容易遇到异常。您可能希望将委托调用包装在异常处理程序中,并考虑如何传播发生的异常(报告第一个异常、生成聚合异常等)
使用
BeginInvoke()
调用deltage将从线程队列中删除一个线程以开始运行事件。这意味着事件将始终从主UI线程触发。这可能会使某些事件处理程序场景更难处理(例如,更新UI)。处理程序需要意识到他们需要调用SynchronizationContext.Send()
或.Post()
与主UI线程同步。当然,所有其他多线程编程陷阱也适用。在思考了一段时间后,我得出结论,在Windows窗体控件中执行异步事件可能不是一个好主意。应在UI线程上引发Windows窗体事件。否则会给客户机带来不必要的负担,并可能导致AsyncResult
对象和异步异常混乱
让客户端启动自己的异步处理(使用BackgroundWorker
或其他一些技术)或同步处理事件更为简洁
当然也有例外<例如,code>System.Timers.Timer会在线程池线程上引发
已用
事件。但是,最初的通知会出现在池线程中。看起来一般的规则是:在获得初始通知的同一线程上引发事件。至少,这是最适合我的规则。这样,泄漏对象就毫无疑问了。为什么要异步调度?您是否做过性能分析,表明这将是有益的?如果不是,多个线程可能会使情况变得更糟。是的,我已经做了性能分析,以确定异步处理是否会带来好处。不幸的是,MSDN线程不是确定的,但它为我提供了一些可以跟踪这一点的指针。是的,客户端(即事件订阅者)的要求准备好处理池线程上的通知在这里很可能是一个杀手。我认为客户端应该异步订阅,如果他们愿意的话,而不是让控制强制订阅。