Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call Method From BackgroundWorker

I hate that my first question seems to have been answered many times, but I'm still having a tough time getting my head around how to call a method using BackgroundWorker.

I'm processing a very large text file using a series of classes and methods. The entire process is kicked off after the user selects a tool strip item. Sequentially, it goes like this:

  • User selects the tool strip item
  • User selects a file to be processed via a dialog box
  • The action starts

I think I can wrap everything into BackgroundWorker from the moment the user pops the initial dialog box, but what I'd like to do for now is just put the method where all the heavy lifting is done into its own instance of BackGroundWorker. I'll add a ProgressBar, too, but I think I can handle that if I can just get the BackgroundWorker process rolling.

From the top (pseudocode used for example purposes. Much omitted for brevity):

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++; 
    }  
} 

I've created an instance of BackgroundWorker...:

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

...and I've tried too many things to list, so I've gone back to the beginning for the presentation above.

If I'm understanding BackgroundWorker, I'll need to do the following:

  • Replace processItem(stringA, stringB) in the above code with something like:

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

...and then do some type of DoWork call? ...and then do some type of RunWorkerCompleted call?

Not sure why my brain is freezing, but I'm embarrassed at the amount of time I've spent on this with no result. Any help would be greatly appreciated. Without StackOverflow, I would have been DOA a long time ago.

FYI: I've referenced other SO posts, MSDN, and DotNetPerls examples. I'm just missing something conceptually, I suppose.

like image 566
LovinItAll Avatar asked Apr 22 '13 17:04

LovinItAll


1 Answers

Replace processItem(stringA, stringB) in the above code with something like...

No, that's how you got in trouble. You most definitely want to move the processFile() call to the worker. There is no perceivable benefit from running processItem() in a worker, at least not in the snippet you posted. And doing so is difficult, it would require starting more than one worker. One for each item. Having a lot of workers that each do little work is not very healthy. If it is really necessary then you don't want to use BackgroundWorker, you'll want an entirely different approach with several Threads that consume packets of work from a thread-safe queue. Don't go there if you can avoid it.

The only non-trivial problem to solve is passing the string that processFile() needs. Luckily BackgroundWorker.RunWorkerAsync() has an overload that takes a single object. Pass your string. Obtain its value in your DoWork event handler, casting e.Argument back to a string. Thus:

    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;
    }
like image 78
Hans Passant Avatar answered Sep 29 '22 00:09

Hans Passant