BackgroundWorker进程需要引发在主(UI)线程上处理的自定义事件

本文关键字:线程 处理 事件 自定义 UI 进程 BackgroundWorker | 更新日期: 2023-09-27 18:16:45

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

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

我的解析器进程可以在后台执行简单地引发一个事件吗?或者必须是SynchronizationContext。当前从主UI线程传递到我的解析器类,然后使用Post方法?

BackgroundWorker进程需要引发在主(UI)线程上处理的自定义事件

如果您绝对必须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线程上运行的原因是,为了方便起见,这些事件实际上是由BackgroundWorker在UI线程上引发的。是的,您可以通过捕获SynchronizationContext,然后在自定义解析器类中发送/发送事件引发逻辑来生成类似的行为。或者,如果您选择在后台线程上引发事件,您可以始终在WPF组件的处理程序中使用Dispatcher.Invoke