简体   繁体   English

异步引发事件

[英]Raising events asynchronously

I'm looking into options for doing asynchronous event dispatching in a component that has many subscribers to its events.我正在研究在具有许多事件订阅者的组件中进行异步事件分派的选项。 In perusing the options, I ran across this example:在仔细阅读这些选项时,我遇到了这个例子:

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);
    }
}

Beyond the older syntax (the sample was from .NET 1.1), it looks to me like this is a serious resource leak.除了较旧的语法(示例来自 .NET 1.1)之外,在我看来这是一个严重的资源泄漏。 There's no completion method, no polling for completion, or any other way that EndInvoke will be called.没有完成方法,没有完成轮询,或任何其他将调用EndInvoke的方式。

My understanding is that every BeginInvoke must have a corresponding EndInvoke .我的理解是每个BeginInvoke都必须有一个对应的EndInvoke Otherwise there are pending AsyncResult object instances floating around, along with (potentially) exceptions that were raised during the asynchronous events.否则,将有待处理的AsyncResult object 实例以及在异步事件期间引发的(可能)异常。

I realize that it's easy enough to change that by supplying a callback and doing an EndInvoke , but if I don't need to.我意识到通过提供回调和执行EndInvoke来改变它很容易,但如果我不需要的话。 . . . .

Handling the asynchronous exeptions is another matter entirely, and, combined with the need to synchronize with the UI thread (ie InvokeRequired , etc.) could very well tank the whole idea of doing these asynchronous notifications.处理异步异常完全是另一回事,并且结合与 UI 线程(即InvokeRequired等)同步的需要,可以很好地解决执行这些异步通知的整个想法。

So, two questions:所以,两个问题:

  1. Am I correct in believing that every BeginInvoke requires a corresponding EndInvoke ?我认为每个BeginInvoke都需要相应的EndInvoke是否正确?
  2. Beyond what I've noted above, are there other pitfalls to doing asynchronous event notifications in Windows Forms applications?除了我上面提到的之外,在 Windows Forms 应用程序中执行异步事件通知是否还有其他陷阱?

A call to BeginInvoke() should be paired with a EndInvoke() but not doing it will not result in a resource leak.BeginInvoke()的调用应与EndInvoke()配对,但不这样做不会导致资源泄漏。 The IAsyncResult returned by BeginInvoke() will be garbage collected. BeginInvoke()返回的IAsyncResult将被垃圾收集。

The biggest pitfall in this code is you are highly exposed to exceptions terminating the application.这段代码中最大的缺陷是您很容易受到终止应用程序的异常的影响。 You might want to wrap the delegate invocation in an exception handler and put some thought into how you want to propagate the exceptions that happen (report the first, produce an aggregate exception, etc).您可能希望将委托调用包装在异常处理程序中,并考虑如何传播发生的异常(报告第一个异常,产生聚合异常等)。

Invoking a deletage using BeginInvoke() will take a thread off the thread queue to start running the event.使用BeginInvoke()调用删除将从线程队列中取出一个线程以开始运行事件。 This means that the event will always fire off the main UI thread.这意味着该事件将始终触发主 UI 线程。 This might make some event handler scenarios harder to handle (eg updating the UI).这可能会使某些事件处理程序场景更难处理(例如更新 UI)。 Handlers would need to realize they need to call SynchronizationContext.Send() or .Post() to synchronize with the primary UI thread.处理程序需要意识到他们需要调用SynchronizationContext.Send().Post()来与主 UI 线程同步。 Of course all other multi-thread programming pitfalls also apply.当然,所有其他多线程编程陷阱也适用。

After thinking about this for a while, I came to the conclusion that it's probably a bad idea to do asynchronous events in Windows Forms controls.考虑了一会儿,我得出的结论是,在 Windows Forms 控件中执行异步事件可能是个坏主意。 Windows Forms events should be raised on the UI thread. Windows Forms 事件应在 UI 线程上引发。 Doing otherwise presents an undue burden on clients, and possibly makes a mess with AsyncResult objects and asynchronous exceptions.否则会给客户端带来过度的负担,并且可能会弄乱AsyncResult对象和异步异常。

It's cleaner to let the clients fire off their own asynchronous processing (using BackgroundWorker or some other technique), or handle the event synchronously.让客户端触发他们自己的异步处理(使用BackgroundWorker或其他一些技术)或同步处理事件会更干净。

There are exceptions, of course.当然,也有例外。 System.Timers.Timer , for example, raises the Elapsed event on a thread pool thread.例如, System.Timers.Timer在线程池线程上引发Elapsed事件。 But then, the initial notification comes in on a pool thread.但是随后,初始通知来自池线程。 It looks like the general rule is: raise the events on the same thread that got the initial notification.看起来一般规则是:在获得初始通知的同一线程上引发事件。 At least, that's the rule that works best for me.至少,这是最适合我的规则。 That way there's no question about leaking objects.这样就没有关于泄漏对象的问题。

  1. No. EndInvoke is only required if a return type is specified.不需要。仅当指定了返回类型时才需要 EndInvoke。 Check this out: thread .看看这个: 线程 Also, I posted this thread which is semi related.另外,我发布了这个半相关的线程

  2. I really cant help you with that one: .-) sorry.我真的无法帮助你:.-) 抱歉。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM