简体   繁体   English

聪明的事件访问器-在其注册的线程上触发处理程序?

[英]Clever event accessors - to fire handlers on the thread they were registered?

Have just had an idea, I haven't seen it before, wondering if you guys thought it was a good idea, if it exists, any common pitfalls etc. - and also how to implement it. 刚刚有一个主意,我以前没看过,想知道你们是否认为这是个好主意,如果存在,任何常见的陷阱等-以及如何实施。

There are several times I've found myself subscribing to an event from the UI thread that will be called from a different thread - for example, notification of a service call completing. 有好几次我发现自己从UI线程中订阅了一个事件,该事件将从另一个线程中调用-例如,服务调用完成的通知。

'My' idea would be to store the current Dispatcher in the add block along with the handler delegate, then when the event is 'fired', perform some extra logic/checks to see if there was a dispatcher associated with the handler, and Invoke on it if necessary. “我的想法”是将当前的Dispatcher与处理程序委托一起存储在add块中,然后在事件被“触发”时,执行一些额外的逻辑/检查以查看是否有与该处理程序相关联的调度程序,并Invoke如果需要的话。

Of course it would only work on threads with a Dispatcher (or Forms equivalent - something with a message pump I guess). 当然,它仅适用于具有Dispatcher线程(或等效于Forms的线程-我猜它带有消息泵)。 I guess the usefulness and cleanliness depends on whether the event subscriber should have to worry about the thread the handler is called or not? 我猜是否有用和干净取决于事件订阅者是否应该担心处理程序被调用的线程?


Edit: Sounds like it's not such a bad thing then - additionally does anyone have any idea how to implement? 编辑:听起来那不是一件坏事-另外,有人不知道如何实现吗? Using Delegate.Combine how could you call each handler on a different Dispatcher , for example? 例如,使用Delegate.Combine如何调用不同Dispatcher上的每个处理程序? Would you instead store delegates in a composite object in a List , and invoke them in turn in the On(Whatever) method, or is there something nicer? 您是将委托存储在List的复合对象中,然后在On(Whatever)方法中依次调用它们,还是有更好的方法?

...Looking at the BackgroundWorker source in Reflector, there's nothing to Invoke: ...查看Reflector中的BackgroundWorker源,没有什么要调用的:

protected virtual void OnProgressChanged(ProgressChangedEventArgs e)
{
    ProgressChangedEventHandler handler = (ProgressChangedEventHandler) base.Events[progressChangedKey];
    if (handler != null)
    {
        handler(this, e);
    }
}

Unless I'm missing something? 除非我想念什么?


So then BackgroundWorker does it with an AsyncOperation . 因此,然后BackgroundWorker使用AsyncOperation How about a general solution, just for event handlers, in the event accessors? 仅针对事件处理程序的事件访问器中的常规解决方案怎么样? BackgroundWorker can get away with the way it works because a method is called from the client - in the more general case, the only time you'll have access to the handler's thread is in the event accessor? BackgroundWorker可以摆脱它的工作方式,因为从客户端调用了一个方法-在更一般的情况下,您唯一可以访问处理程序线程的时间是在事件访问器中? :) :)

As far as I know, that's exactly what the BackgroundWorker is doing in its RunWorkerCompleted and ProgressChanged events. 据我所知,这正是BackgroundWorker在其RunWorkerCompletedProgressChanged事件中所做的事情。 So it can't be that bad. 因此,不会那么糟。
I can't find a real proof, that the BackgroundWorker is doing it, I just read it somewhere. 我找不到真正的证据证明BackgroundWorker正在这样做,只是在某处阅读过。 When you google for it , you will find more hints. 当您使用google搜索时 ,会发现更多提示。 If someone can provide a link, I would be happy. 如果有人可以提供链接,我会很高兴。

UPDATE: 更新:
Because it isn't so easy to find this behavior in the BackgroundWorker, I provide my analysis: 由于在BackgroundWorker中发现这种行为并不容易,因此我提供了以下分析:
The BackgroundWorker is using an AsyncOperation for raising the events. BackgroundWorker使用AsyncOperation引发事件。 Inside this class, the events are posted to a SynchronizationContext . 在此类内部,事件被发布到SynchronizationContext Only then are the methods OnProgressChanged and OnRunWorkerCompleted executed. 然后, OnRunWorkerCompleted执行OnProgressChangedOnRunWorkerCompleted方法。 This means, those methods are already executed on the right thread. 这意味着这些方法已经在正确的线程上执行。

In some more detail, the following happens, when RunWorkerAsync is called: 更详细地说,在调用RunWorkerAsync时,会发生以下情况:

  1. An AsyncOperation instance is created via AsyncOperationManager.CreateOperation . 通过AsyncOperationManager.CreateOperation创建一个AsyncOperation实例。 This saves the current SynchronizationContext . 这将保存当前的SynchronizationContext As we are still in the UI thread, this is the context of the UI thread. 由于我们仍在UI线程中,因此这是UI线程的上下文。
  2. The background operation is started and calls into the private method WorkerThreadStart . 启动后台操作,并调用私有方法WorkerThreadStart This method is running in the background thread and executes OnDoWork which in turn raises the DoWork event. 此方法在后台线程中运行,并执行OnDoWork ,这将引发DoWork事件。 This means, the DoWork event is not raised in the UI thread. 这意味着在UI线程中不会引发DoWork事件。
  3. After OnDoWork completed, the PostOperationCompleted method of the AsyncOperation instance is executed which in turn calls AsyncOperation.Post which calls SynchronizationContext.Post which in turn will call indirectly OnRunWorkerCompleted on the UI thread. OnDoWork完成后,将执行AsyncOperation实例的PostOperationCompleted方法,该方法依次调用AsyncOperation.PostSynchronizationContext.Post ,后者又将在UI线程上间接调用OnRunWorkerCompleted
  4. When ReportProgress is called, a similar thing happens: AsyncOperation.Post is called directly and will invoke the OnProgressChanged method on the UI thread. 调用ReportProgress ,会发生类似的事情:直接调用AsyncOperation.Post ,它将在UI线程上调用OnProgressChanged方法。

AsyncOperation and AsyncOperationManager are public and can be used to implement a similar behavior in your classes. AsyncOperationAsyncOperationManager是公共的,可用于在您的类中实现类似的行为。

我已经完成了与Castle IsInvokeRequired/Invoke类似的操作,在此它拦截呼叫并对其进行IsInvokeRequired/Invoke

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

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