从BackgroundWorker调用方法

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,我早就失败了。

FYI: 我已经参考了其他SO贴子,MSDN和DotNetPerls示例。 我只是在概念上缺失了一些东西,我想。


不要忘记绑定所有事件。 bw.DoWork += new DoWorkEventHandler(bw_DoWork); bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged); bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); - Scott Adams
好问题。帮了我很多。 - Ace McCloud
3个回答

3
将上述代码中的processItem(stringA, stringB)替换为以下内容: 不,这样做会让你遇到麻烦。在worker中调用processFile()是最好的选择,至少在你发表的片段中没有明显的好处。而且这样做很困难,需要启动多个worker,每个item都需要。拥有许多完成少量工作的worker是不太好的。如果真的必要,那么你不应该使用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;
    }

谢谢 - 我很感激你的回答,包括解释。事实上,传递变量FileName是我一直难以理解的问题,但我在问题描述中没有注意到。另一个示例提供了完整的代码解决方案,所以选择最佳答案很困难。我觉得这样做非常懒惰,所以你因为你的评论而获得了认可。虽然我已经让一切正常运行,但我希望我能够完全理解为什么它能够工作,但这是另一个问题。最好的问候-L.I.A. - LovinItAll

1
BackgroundWorker bgw;

在加载事件或构造函数中:
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
    }

1
我选择了另一个答案,因为它的解释更好,但请注意,你的代码直接导致了我的解决方案能够工作,特别是在Load事件中包含BackgroundWorker信息的指令。选择一个答案非常困难 - 我甚至睡觉前都在考虑。如果没有你的代码,我会花更多时间来解决问题,但如果没有我选择的答案中的解释,我只会复制和粘贴解决方案而不会进一步理解。请知道,不选择你的答案是一个艰难的决定...再次感谢。 - LovinItAll
我在想我们是否可以从异步工作程序中调用一个函数,就像这里的processFile方法一样,还是它也会在新线程上运行或在主线程上运行? - Anil

1
启动新的后台工作线程是一项昂贵的操作。您不希望为每个循环迭代启动一个线程。相反,将整个循环放在单个后台工作线程的范围内。
当运行 ToolStripMenuItem_Click 时创建后台工作线程,让 processFile 成为 DoWork 事件处理程序中要执行的内容。
确保在进行此工作时,您只是在执行该工作,而不是更新UI。您需要将业务逻辑与用户界面分开。如果要使用当前进度更新UI,则调用 ReportProgress 并确保有事件处理程序来正确更新UI。
如果需要在完成所有工作后更新UI,则可以在 RunWorkerCompleted 事件处理程序中这样做。如果您正在执行的工作生成某些结果以用于更新UI,请使用后台工作线程的 Result 属性将其从 DoWork 方法传递到完成处理程序中。

谢谢,您提出的观点已经被记录下来,并有助于我理解生成新线程及其一般影响。感谢您抽出时间提供您的意见,尽管我选择了另一个更完整地回答问题的答案(您给了我比我应得的更多信任,认为我能够在没有进一步信息的情况下实现解决方案。换句话说,是我的问题,而不是您的问题 :))。 - LovinItAll

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接