Minato
Minato

Reputation: 352

How To Know When A Group Of BackgroundWorkers Are Done

I have a list of URLs and for each URL a BackgroundWorker is created to handle downloading data and updating its respective ProgressBar.

My question is how can I determine when all of the downloads have completed? As of right now, I only can find out when each BackgroundWorker finishes individually but not as a group.

for (int i = 0; i < videos.Count; i++)
{
    int index = i;
    string path = Path.Combine(Settings.Default.DownloadLocation,
        videos[index].Title + videos[index].AudioExtension);
    BackgroundWorker worker = new BackgroundWorker
    {
        WorkerReportsProgress = true,
        WorkerSupportsCancellation = false
    };
    worker.DoWork += delegate
    {
        AudioDownloader audioDownloader = new AudioDownloader(videos[index], path);
        audioDownloader.AudioExtractionProgressChanged += (obj, args) =>
        {
            int progress = (int)Math.Round(85 + args.ProgressPercentage * 0.15);
            worker.ReportProgress(progress);
        };
        audioDownloader.DownloadProgressChanged += (obj, args) =>
        {
            int progress = (int)Math.Round(args.ProgressPercentage * 0.85);
            worker.ReportProgress(progress);
        };
        audioDownloader.Execute();
    };
    worker.ProgressChanged += (obj, args) =>
    {
        progressi[index].Value = args.ProgressPercentage;
    };
    worker.RunWorkerCompleted += delegate
    {
        Notify(videos[index].Title + " is finished downloading.");
    };
    worker.RunWorkerAsync();
}

I was considering doing something like this:

Task[] downloadTasks = new Task[videos.Count];
for (int i = 0; i < videos.Count; i++)
{
    int index = i;
    downloadTasks[index] = Task.Factory.StartNew(() =>
        {
            // Create BackgroundWorker and run it
        });
}
Task.Factory.ContinueWhenAll(downloadTasks, (tasks) =>
{
    // All the downloads are complete!
});

But the ContinueWhenAll runs before the downloads are actually complete, so I'm not sure what to do.

Upvotes: 1

Views: 1013

Answers (3)

Hamid Pourjam
Hamid Pourjam

Reputation: 20754

when you are doing this

for (int i = 0; i < videos.Count; i++)
{
    int index = i;
    downloadTasks[index] = Task.Factory.StartNew(() =>
        {
            // Create BackgroundWorker and run it
        });
}

you create some background worker and tell them to do something in another thread. so just after telling them to do their work your tasks' job is finished so all of them (tasks) will be finished in a moment and

Task.Factory.ContinueWhenAll(downloadTasks, (tasks) =>
   {
       // All the downloads are complete!
   });

checks if all tasks have done their work? the answer is: Yes! they all created a background worker and tell them what to do.

you can use some kind of flags, assign each background worker an Id and set a boolean in an array when it has finished it's work. check if all of them has finished their work and then go on. you can do this in your Notify method. check if all of the flags are true and then do what you want there.

Upvotes: 2

TH Todorov
TH Todorov

Reputation: 1159

Another simple workaround - use a counter that keeps track of all your backgroundworkers.

   int counter = 0;

In the DoWork of each backgroundworker:

    private void bwDoWork(object sender, DoWorkEventArgs e)
    {
        Interlocked.Increment(ref counter);
        //do other stuff
    }

and in each RunWorkerCompleted, decrement the counter and check if zero, which tells you that all backgroundworkers are done

    private void bwRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Interlocked.Decrement(ref counter);
        if (counter == 0)
        {
            //All backgroundworkers are done, so do something
        }
    }

Upvotes: 1

sk_
sk_

Reputation: 2273

Using a BackgroundWorker is somewhat outdated since .Net 4.5 by task-returning async methods. This is nicely explained why here.

In your use case, you would probably end up with something as simple as:

public async Task DownloadTask()
{
   Task[] tasks = new Task[2];
   tasks[0] = DoSomeStuff();
   tasks[1] = DoSomeStuff();

   await Task.WhenAll(tasks);
}

Upvotes: 1

Related Questions