【问题标题】:BackgroundWorker process needs to raise custom events to be handled on the main (UI) threadBackgroundWorker 进程需要引发要在主 (UI) 线程上处理的自定义事件
【发布时间】:2013-06-02 02:03:41
【问题描述】:

我有一个用于大型 CSV 文件的解析器类。 parse 方法逐行读取大文件的工作是在 backgroundWorker 中完成的。使用backgroundWorker.ReportProgress 方法将完成百分比信息传递给UI 线程,这样我的表单上的进度条就可以完成它的工作。效果很好。

但是,我还想引发一个自定义事件,该事件将从 CSV 文件的第一行中获取的字段名列表发送回 UI (WPF),以便可以将它们放置在下拉列表中。如果解析器遇到格式错误的行或其他障碍,我还想通过事件通知用户。

我在后台执行的解析器进程能否简单地引发一个事件?还是必须将 SynchronizationContext.Current 从主 UI 线程传递给我的解析器类,然后它会使用 Post 方法?

【问题讨论】:

  • 创建一个继承自BackgroundWorker的类,创建你自己的事件,从你的worker的DoWork方法调用它?
  • @gunr2171 - 这并不能解决问题,因为如果您在 DoWork 处理程序中执行此操作,事件仍将在后台线程上引发(因此其处理程序也将在后台线程上运行)。仍然需要一些在 UI 线程上调度工作的方法。
  • @KirillShlenskiy,是的,您从 DoWork 方法引发事件,但您从工作人员外部订阅它。你必须做一些调用(我认为)才能让它回到主线程,但这不应该挂起工作线程。
  • 考虑使用 Task 和 async/await 代替。
  • @gunr2171 对,你没有详细说明调用部分......很明显 OP 没有得到那个部分......

标签: c# wpf backgroundworker synchronizationcontext


【解决方案1】:

如果您绝对必须在您的 DoWork 处理程序中安排 UI 线程上的工作,最简单的方法是使用 Dispatcher.Invoke

public partial class MainWindow : Window
{
    public MainWindow()
    {
        this.InitializeComponent();

        var worker = new BackgroundWorker();

        worker.DoWork += (s, e) =>
        {
            // Suppose we've done some processing and need to notify the UI.
            // We're on the background thread, so we can't manipulate the UI directly.
            this.Dispatcher.Invoke(new Action(() =>
            {
                // This will actually run on the UI thread.
                this.Label.Content = "hello from the background thread.";
            }));
        };

        worker.RunWorkerAsync();
    }
}

对于事件及其执行线程同步的神奇能力似乎有很多困惑,所以请允许我吐槽一下。

事件是美化的多播委托。当您引发一个事件时,其调用列表中的每个委托都会被调用,在引发该事件的线程上。因此,如果您在自定义解析器类中创建一个事件只是为了让它从 DoWork 处理程序中引发,该事件的处理程序仍将在后台线程上执行,您仍然需要找到一种方法来切换到UI 同步上下文 - 通过在新事件的处理程序中执行一些 Invoke/SynchronizationContext.Post/Send 魔术,或者通过调用/发布/发送实际的事件引发逻辑。

RunWorkerCompletedProgressChanged 等开箱即用事件的处理程序在 UI 线程上运行的原因是这些事件实际上是由 UI 线程上引发的 BackgroundWorker 为您提供方便。是的,您可以通过捕获SynchronizationContext 然后在您的自定义解析器类中发布/发送事件引发逻辑来产生类似的行为。或者,如果您选择在后台线程上引发事件,您始终可以在 WPF 组件的处理程序中使用Dispatcher.Invoke

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多