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

本文关键字:线程 注册 程序 处理 事件 访问 | 更新日期: 2023-09-27 17:49:00

刚刚有一个想法,我以前没有见过,想知道你们是否认为这是一个好主意,如果存在,任何常见的陷阱等 - 以及如何实现它。

有几次我发现自己从 UI 线程订阅了一个事件,该事件将从不同的线程调用 - 例如,服务调用完成的通知。

"我"的想法是将当前Dispatcher与处理程序委托一起存储在add块中,然后在事件"触发"时,执行一些额外的逻辑/检查以查看是否有与处理程序关联的调度程序,并在必要时对其进行Invoke

当然,它仅适用于具有Dispatcher(或表单等效的线程 - 我猜带有消息泵的东西(。 我想有用性和清洁度取决于事件订阅者是否应该担心调用处理程序的线程?


编辑:听起来这不是一件坏事 - 此外,有人知道如何实施吗? 例如,使用Delegate.Combine如何在不同的Dispatcher上调用每个处理程序? 您是否会将委托存储在List的复合对象中,并在On(Whatever)方法中依次调用它们,还是有更好的方法?

。查看 Reflector 中的BackgroundWorker源,没有什么可调用的:

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

除非我错过了什么?


所以BackgroundWorkerAsyncOperation来做. 事件访问器中仅针对事件处理程序的通用解决方案怎么样? BackgroundWorker可以摆脱它的工作方式,因为从客户端调用了一个方法 - 在更一般的情况下,您唯一可以访问处理程序线程的时间是在事件访问器中?:)

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

据我所知,这正是BackgroundWorker在其RunWorkerCompletedProgressChanged事件中所做的。所以它不可能那么糟糕。
我找不到真正的证据,BackgroundWorker正在这样做,我只是在某处读到它。当你谷歌搜索它时,你会发现更多的提示。如果有人可以提供链接,我会很高兴。

更新:
因为在后台工作者中找到这种行为并不容易,所以我提供了我的分析:
BackgroundWorker正在使用AsyncOperation来引发事件。在此类中,事件将发布到SynchronizationContext。只有这样,方法才会OnProgressChangedOnRunWorkerCompleted执行。这意味着,这些方法已经在正确的线程上执行。

更详细地说,调用 RunWorkerAsync 时会发生以下情况:

  1. AsyncOperation实例是通过 AsyncOperationManager.CreateOperation 创建的。这将节省当前SynchronizationContext。由于我们仍在 UI 线程中,因此这是 UI 线程的上下文。
  2. 后台操作启动并调用私有方法WorkerThreadStart。此方法在后台线程中运行并执行OnDoWork进而引发DoWork事件。这意味着,不会在 UI 线程中引发DoWork事件。
  3. OnDoWork完成后,将执行AsyncOperation实例的 PostOperationCompleted 方法,该方法依次调用AsyncOperation.Post调用SynchronizationContext.Post进而间接调用 UI 线程上的OnRunWorkerCompleted
  4. 调用 ReportProgress 时,会发生类似的事情:直接调用AsyncOperation.Post,并将在 UI 线程上调用 OnProgressChanged 方法。

AsyncOperationAsyncOperationManager 是公共的,可用于在类中实现类似的行为。

我对Castle DynamicProxy做了类似的事情,它拦截调用并对它们进行IsInvokeRequired/Invoke