C# 聪明的事件访问器-在它们注册的线程上触发处理程序?
我刚刚有了一个想法,我以前没见过,想知道你们是否认为这是一个好主意,它是否存在,是否有任何常见的陷阱等等,以及如何实施它 有好几次我发现自己从UI线程订阅了一个事件,该事件将从另一个线程调用——例如,服务调用完成的通知 “我的”想法是将当前的C# 聪明的事件访问器-在它们注册的线程上触发处理程序?,c#,multithreading,events,delegates,dispatcher,C#,Multithreading,Events,Delegates,Dispatcher,我刚刚有了一个想法,我以前没见过,想知道你们是否认为这是一个好主意,它是否存在,是否有任何常见的陷阱等等,以及如何实施它 有好几次我发现自己从UI线程订阅了一个事件,该事件将从另一个线程调用——例如,服务调用完成的通知 “我的”想法是将当前的Dispatcher与处理程序委托一起存储在add块中,然后当事件被“激发”时,执行一些额外的逻辑/检查以查看是否有与处理程序关联的调度程序,并在必要时调用它 当然,它只能在具有调度程序的线程上工作(或者是具有消息泵的等价形式)。我想有用性和干净性取决于事件
Dispatcher
与处理程序委托一起存储在add
块中,然后当事件被“激发”时,执行一些额外的逻辑/检查以查看是否有与处理程序关联的调度程序,并在必要时调用它
当然,它只能在具有调度程序的线程上工作(或者是具有消息泵的等价形式)。我想有用性和干净性取决于事件订阅者是否应该担心处理程序调用的线程
编辑:听起来这并不是一件坏事——另外,有人知道如何实现吗?例如,使用Delegate.Combine
如何在不同的调度程序上调用每个处理程序?您是将委托存储在列表中的复合对象中,然后在On(Whatever)
方法中依次调用它们,还是有更好的方法
…查看Reflector中的BackgroundWorker
源代码,没有什么可调用的:
protected virtual void OnProgressChanged(ProgressChangedEventArgs e)
{
ProgressChangedEventHandler handler = (ProgressChangedEventHandler) base.Events[progressChangedKey];
if (handler != null)
{
handler(this, e);
}
}
除非我遗漏了什么
因此,BackgroundWorker
通过一个AsyncOperation
来完成它。在事件访问器中为事件处理程序提供一个通用解决方案怎么样BackgroundWorker
可以摆脱它的工作方式,因为从客户端调用了一个方法-在更一般的情况下,只有在事件访问器中才能访问处理程序的线程:) 据我所知,这正是BackgroundWorker
在其RunWorkerCompleted
和ProgressChanged
事件中所做的。所以不会那么糟糕。
我找不到真正的证据,BackgroundWorker
正在做这件事,我只是在某个地方读到了。当你开始时,你会发现更多的提示。如果有人能提供一个链接,我会很高兴
更新:
因为在BackgroundWorker中发现这种行为并不容易,所以我提供了我的分析:
BackgroundWorker
正在使用AsyncOperation
来引发事件。在这个类中,事件被发布到SynchronizationContext
。只有这样,才能执行方法OnProgressChanged
和OnRunWorkerCompleted
。这意味着,这些方法已经在正确的线程上执行
更详细地说,当调用RunWorkerAsync
时,会发生以下情况:
通过AsyncOperationManager.CreateOperation
创建一个AsyncOperation
实例。这将保存当前的同步上下文。由于我们仍然在UI线程中,这是UI线程的上下文
后台操作启动并调用私有方法WorkerThreadStart
。此方法在后台线程中运行,并执行OnDoWork
,从而引发DoWork
事件。这意味着,DoWork
事件不是在UI线程中引发的
在OnDowWork
完成后,执行AsyncOperation
实例的PostOperationCompleted
方法,该方法依次调用AsyncOperation.Post
,该方法调用SynchronizationContext.Post
,该方法反过来将间接调用UI线程上的OnRunWorkerCompleted
调用ReportProgress
时,会发生类似的情况:AsyncOperation.Post
直接被调用,并将在UI线程上调用OnProgressChanged
方法
和是公共的,可以用于在类中实现类似的行为。据我所知,这正是BackgroundWorker
在其RunWorkerCompleted
和ProgressChanged
事件中所做的。所以不会那么糟糕。
我找不到真正的证据,BackgroundWorker
正在做这件事,我只是在某个地方读到了。当你开始时,你会发现更多的提示。如果有人能提供一个链接,我会很高兴
更新:
因为在BackgroundWorker中发现这种行为并不容易,所以我提供了我的分析:
BackgroundWorker
正在使用AsyncOperation
来引发事件。在这个类中,事件被发布到SynchronizationContext
。只有这样,才能执行方法OnProgressChanged
和OnRunWorkerCompleted
。这意味着,这些方法已经在正确的线程上执行
更详细地说,当调用RunWorkerAsync
时,会发生以下情况:
通过AsyncOperationManager.CreateOperation
创建一个AsyncOperation
实例。这将保存当前的同步上下文。由于我们仍然在UI线程中,这是UI线程的上下文
后台操作启动并调用私有方法WorkerThreadStart
。此方法在后台线程中运行,并执行OnDoWork
,从而引发DoWork
事件。这意味着,DoWork
事件不是在UI线程中引发的
在OnDoWork
完成后,执行AsyncOperation
实例的PostOperationCompleted
方法,该方法依次调用AsyncOperation.Post
它调用SynchronizationContext.Post