Tim
Tim

Reputation: 51

How do I know when multiple background workers are finished?

So right now I have a method that calls a different method three times.

List<BackgroundWorker> bgws = new List<BackgroundWorker>();
AutoResetEvent _workerCompleted = new AutoResetEvent(false);

void methodA () {
    methodB(); //bw1
    methodB(); //bw2
    methodB(); //bw3

    //wait for the background workers that were kicked off above to finish
    _workerCompleted.WaitOne();

    Console.Writeline("hey");
}

void methodB() {
    BackgroundWorker bw = new BackgroundWorker();
    bw.WorkerReportsProgress = true;
    bw.WorkerSupportsCancellation = true;
    bgws.Add(bw);

    bw.DoWork += (sender, DoWorkEventArgs) => { bwWork(sender, DoWorkEventArgs, info); };
    bw.ProgressChanged += (sender, ProgressChangedEventArgs) => { bwProgressChanged(sender, ProgressChangedEventArgs, info); };
    bw.RunWorkerCompleted += (sender, RunWorkerCompletedEventArgs) => { bwCompleted(sender, RunWorkerCompletedEventArgs, info); };

    bw.RunWorkerAsync();

}

The bwWork and bwProgressChanged events shouldn't matter too much. The former is just processing a file, the latter is updating a progress bar and label.

private void bwCompleted(object senderr, 
System.ComponentModel.RunWorkerCompletedEventArgs e, SenderInfo info)
{
    BackgroundWorker bw = (BackgroundWorker)senderr;

    Console.WriteLine(Path.GetFileName(info.path) + " : Finished.");

    bgws.Remove(bw); //remove the bw from the list
    //remove events from bw
    bw.DoWork -= (sender, DoWorkEventArgs) => { bwWork(sender, DoWorkEventArgs, info); };
    bw.ProgressChanged -= (sender, ProgressChangedEventArgs) => { bwProgressChanged(sender, ProgressChangedEventArgs, info); };
    bw.RunWorkerCompleted -= (sender, RunWorkerCompletedEventArgs) => { bwCompleted(sender, RunWorkerCompletedEventArgs, info); };
    //dispose bw
    bw.Dispose();
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
    if (bgws.Count == 0) //if there are no more bw's, we're good to go
    {
        MessageBox.Show("Done.");
        bgws.Clear();
        _workerCompleted.Set();
    }

}

So in the end, I want all of the background workers to finish what they're doing before "hey" is printed out. I tried using the AutoResetEvent and ManualResetEvent, yet nothing seemed to happen when they were present. What I mean is that none of the background workers even ran when I included them.

I would invoke a thread, but I'm a little reluctant because I was told background workers were the thing to use when doing background work and updating UI. Is there any way that I can wait on the three background workers so that I can continue on with my program?

Upvotes: 1

Views: 996

Answers (3)

Jeffrey Elkins
Jeffrey Elkins

Reputation: 21

Know I'm two years late but this is what I did in a similar situation. Created a completed counter. Added one to the completed counter each time a BGW from the list finished. Had a seperate MonitorBGW setup with a while loop kinda like what follows.

While(CompletedCounter < ListBGWs.Count){Thead.Sleep(2000)}
//Put my code I wanted run after they all completed here.

Hope this helps someone :)

Upvotes: 1

NetMage
NetMage

Reputation: 26917

I believe the issue is that the BackgroundWorker does DoWork on a separate thread, but uses the thread it is created on to run ProgressChanged and RunWorkerCompleted events so they can update the UI. But if you already have the UI thread tied up in a WaitOne or other spin, you can't execute those events.

In your bwCompleted when bgws.Count == 0 why don't you call a methodA_AfterDone to handle the read of methodA and just have methodA end after bw3?

Upvotes: 1

Filip Cordas
Filip Cordas

Reputation: 2561

Simple you don't use BackgroundWorker and switch to Tasks. Task.Run(() => Your work ); And after you do that you can use Task.WaitAll(task1,task2); You can switch any EAP to Tasks. And with async you can make this with ease.

Upvotes: -1

Related Questions