如何将我的代码放入后台工作程序?

How can I fit my code into a Background Worker?

我构建了一个执行以下操作的表单:

  1. 抓取目录中的所有文件路径(文件在指定的命名约定中)
  2. 使用分隔符解析文件路径
  3. 输出指定格式的文本文件。

问题是一些目录有数百个演出,这会冻结表格 UI,直到该过程完成。我已经阅读了一些关于 Background workers 的文章,据我了解,代码的数据密集部分属于 DoWork 方法。但是,当我尝试将我的代码实现到 DoWork 方法中时,我 运行 进入了 "Cross-thread operation not valid:" 错误。我查找了错误,但我根本不明白如何在不重新设计程序的整个结构的情况下解决此错误。

我的代码相当冗长,但如果需要我可以提供。

最有可能的罪魁祸首是您正在从后台工作人员访问控件所持有的值。人们既不能从这些对象写入,也不能读取,除非调用返回到 GUI 线程。长话短说,我 运行 在尝试从我的 do work 线程的文本框中 读取值 时遇到了同样的问题,甚至虽然是读取,但失败了。

我建议您从后台工作程序中删除所有数据 access/update 以解决此问题。

这里是我写的关于后台工作者的文章

C# WPF: Linq Fails in BackgroundWorker DoWork Event

C# WPF: Threading, Control Updating, Status Bar and Cancel Operations Example All In One

最好的方法是重新编写代码,使其不使用 UI 组件。

完成后,您可以使用 ReportProgress/ProgressChanged 和 RunWorkerCompleted 刷新 UI。

如果您仍想使用 UI 组件,您可以尝试 Invoke :

this.Invoke(new Action(() => 
    {
        //code that uses UI for instance :
        //this.TextBox1.Text = "test"
    });

当您尝试从 BackGroundWorker 写入窗体上的控件时发生错误。编写控件时需要添加InVoke。请参阅以下网页:https://msdn.microsoft.com/en-us/library/ms171728%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396

您的代码从创建它们的线程以外的线程访问 GUI 控件的属性。 WindowsForms/WPF 在内部检查它,发现您正在从另一个线程访问控件的 属性 并抛出异常。

我建议您使用 MVVM 模式。此外,BackgroundWorker 现在被认为已过时,您应该改用带有 async/await 关键字的任务。以下是我在 WPF 应用程序中的做法:

public class ViewModel : INotifyPropertyChanged
{
    ...

    public string PathInfo { ... } // raises INotifyPropertyChanged.PropertyChanged event from the setter
    public RelayCommand ProcessPathsCommand { get; set; }

    public ViewModel()
    {
        ProcessPathsCommand = new RelayCommand(ProcessPaths);
    }

    public async void ProcessPaths()
    {
        // disable the command, which will lead to disabling a button bound to the command
        ProcessPathsCommand.IsEnabled = false;

        try
        {
            // run processing on another thread
            await Task.Run(() =>
            {
                // emulate hard-work
                Thread.Sleep(5000);
                // update the property on the view model, which will lead to updating a textblock bound to this property                
                // in WPF you can update the bound property right from another thread and WPF will automatically dispatch PropertyChanged event to the main UI thread. In WinForms this would lead to an exception.
                PathInfo = "Here are the results: bla bla bla";
            });
            // In WinForms you would need to update the bound property here:
            // Thanks to Tasks and await this line of code will be executed only after the task is finished
            // PathInfo = resultFromTaskThread;
        }
        finally
        {
            ProcessPathsCommand.IsEnabled = true;
        }
    }

我们中的许多人都记得在 WindowsForms 中我们不得不使用 Control.Invoke() 从另一个线程更新 GUI 控件(我们中的许多人仍然在 WPF 中使用 Dispatcher.Invoke 甚至不需要的时候)。借助 WPF,Microsoft 终于让我们的工作变得更轻松了! WPF 自动将 PropertyChanged 事件分派到主 GUI 线程,所以我们可以简单地从另一个线程更新视图模型上的 属性(绑定到 UI 上的某个控件),属性 setter 将引发 PropertyChanged 事件,WPF 会自动将此事件分派给 UI 线程!
您也可以在这里阅读:
Multithreading and Dispatching in MVVM Applications

如果您需要更多详细信息,请随时告诉我。

如果 TB_path 或 TB_TrackingNum 是 UI 元素(我怀疑它们是),那么您正在尝试访问 UI。

如果您只是将 UI 元素绑定到 public 属性,您可以从 BackgroundWorker 访问 public 属性。