5

我讨厌我的第一个问题似乎已被多次回答,但我仍然很难弄清楚如何使用 BackgroundWorker 调用方法。

我正在使用一系列类和方法处理一个非常大的文本文件。在用户选择一个工具条项目后,整个过程就开始了。依次,它是这样的:

  • 用户选择工具条项目
  • 用户通过对话框选择要处理的文件
  • 行动开始

我想我可以从用户弹出初始对话框的那一刻起将所有内容包装到 BackgroundWorker 中,但我现在想做的只是将完成所有繁重工作的方法放入它自己的 BackGroundWorker 实例中。我也会添加一个 ProgressBar,但我认为如果我可以让 BackgroundWorker 进程滚动,我可以处理它。

从顶部开始(用于示例目的的伪代码。为简洁起见,省略了很多):

private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
     string fileName = openSingleFile.FileName;
     processFile(fileName); 
}

static public void processFile(string fileName)
{
// many vars/loops exist but not shown

    foreach (data in bigData)
    {
        processItem(stringA, stringB); // <-- this method is where the expensive work is done 
        x++; 
    }  
} 

我创建了一个 BackgroundWorker 的实例...:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    // Things go here
}

...而且我已经尝试了太多的东西来列出,所以我回到了上面演示的开头。

如果我了解 BackgroundWorker,我需要执行以下操作:

  • 将上面代码中的 processItem(stringA, stringB) 替换为:

    backgroundWorker1.RunWorkerAsync(processItem(stringA, stringB));

...然后进行某种类型的 DoWork 调用?...然后进行某种类型的 RunWorkerCompleted 调用?

不知道为什么我的大脑会冻僵,但我很尴尬我花了这么多时间却没有结果。任何帮助将不胜感激。如果没有 StackOverflow,我很久以前就会成为 DOA。

仅供参考:我参考了其他 SO 帖子、MSDN 和 DotNetPerls 示例。我想,我只是在概念上遗漏了一些东西。

4

3 回答 3

3

将上面代码中的 processItem(stringA, stringB) 替换为...

不,这就是你遇到麻烦的原因。您绝对希望将 processFile() 调用移至工作人员。在工作人员中运行 processItem() 没有明显的好处,至少在您发布的片段中没有。这样做很困难,需要启动多个工人。每个项目一个。有很多工人,每个人都做很少的工作,这不是很健康。如果确实有必要,那么您不想使用 BackgroundWorker,您将需要一种完全不同的方法,其中包含多个线程,这些线程使用线程安全队列中的工作包。如果可以避免,请不要去那里。

唯一需要解决的重要问题是传递 processFile() 需要的字符串。幸运的是 BackgroundWorker.RunWorkerAsync() 有一个采用单个对象的重载。传递你的字符串。在 DoWork 事件处理程序中获取其值,将 e.Argument 转换回字符串。因此:

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
        string path = (string)e.Argument;
        processFile(path);
    }

    private void processToolStripMenuItem_Click(object sender, EventArgs e) {
        backgroundWorker1.RunWorkerAsync(openSingleFile.FileName);
        processToolStripMenuItem.Enabled = false;
    }

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
        processToolStripMenuItem.Enabled = true;
    }
于 2013-04-22T17:44:41.880 回答
1
BackgroundWorker bgw;

在 Load 事件或构造函数中:

bgw = new BackgroundWorker();
bgw.WorkerReportsProgress = true;
//bgw.WorkerSupportsCancellation = true;

bgw.DoWork += bgw_DoWork;
bgw.ProgressChanged += bgw_ProgressChanged;
bgw.RunWorkerCompleted += bgw_RunWorkerCompleted;

/

    private void ToolStripMenuItem_Click(object sender, EventArgs e)
    {
        string fileName = openSingleFile.FileName;

        bgw.RunWorkerAsync(fileName);
    }

    private void bgw_DoWork(object sender, DoWorkEventArgs e)
    {
        string fileName = (string)e.Argument;

        processFile(fileName);
    }

    private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        int Progress = e.ProgressPercentage;

        //Update progressbar here
    }

    private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        //Job completed
    }
于 2013-04-22T17:29:52.297 回答
1

启动一个新的后台工作者是一项昂贵的操作。您不想为循环的每次迭代都启动一个。相反,将整个循环放在单个后台工作人员的范围内。

运行时ToolStripMenuItem_Click创建您的后台工作人员,processFileDoWork事件处理程序中完成。

确保在做这项工作时你真的只是在做这项工作,而不是更新 UI。您需要将业务逻辑与用户界面分开。如果您想使用当前进度更新 UI,请调用ReportProgress并确保有一个事件处理程序来正确更新 UI。

如果您需要在工作完成后更新 UI,那么您可以在RunWorkerCompleted事件处理程序中执行此操作。如果您正在执行的工作生成了一些用于更新 UI 的结果,请使用Result后台工作人员的属性将其从DoWork方法传递给已完成的处理程序。

于 2013-04-22T17:21:55.237 回答