Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I report progress based on multiple variables in a nested loop (for a progress bar)?

I have a BackgroundWorker and a single ProgressBar. When working, the BackgroundWorker runs through a triple for-loop and reports progress to the ProgressBar.

Currently, the progress that is being reported is only that of the outter-most loop (xProgress), which works, but does not run smoothly. The goal is for the ProgressBar to also account for the progress percentages of the inner loops, so that the ProgressBar updates more smoothly and more accurately.

The DoWork method:

    private void bgw_DoWork(object sender, DoWorkEventArgs e)
    {
        int xMax, yMax, zMax;

        xMax = 10;
        for (int x = 1; x <= xMax; x++)
        {
            yMax = 5;
            for (int y = 1; y <= yMax; y++)
            {
                zMax = new Random().Next(50, 100);
                for (int z = 1; z <= zMax; z++)
                {
                    Thread.Sleep(5); /// The process

                    double xProgress = (double)x / (double)xMax;
                    double yProgress = (double)y / (double)yMax;
                    double zProgress = (double)z / (double)zMax;

                    /// The progress calculation:
                    double progressCalc = xProgress;

                    int progress = (int)(progressCalc * pgb.Maximum);

                    bgw.ReportProgress(progress);
                }
            }
        }
    }
like image 591
DomTheDeveloper Avatar asked Dec 14 '16 14:12

DomTheDeveloper


2 Answers

This works even if you don't know your best/worst case in advance. Any of the max's could be randomized in the same fashion of zMax.

static void F()
{
    var r = new Random();
    int xMax = 10;
    int yMax = 5;
    int zMax;

    for (int x = 0; x < xMax; x++)
    {
        double xProg = (double)x / xMax;

        for (int y = 0; y < yMax; y++)
        {
            double yProg = (double)y / (yMax * xMax);

            zMax = r.Next(50, 100);
            for (int z = 0; z < zMax; z++)
            {
                double zProg = (double)z / (zMax * yMax * xMax);

                var prog = xProg + yProg + zProg;

                Console.WriteLine(prog.ToString("P"));
                // bgw.ReportProgress((int)(prog * pgb.Maximum));
            }
        }
    }

    Console.WriteLine(1.ToString("P")); // Make sure to report the final 100%
    // bgw.ReportProgress((int)pgb.Maximum);
}

Btw, I'd replace the pgb.Maximum's with 1 and in the OnProgressHandler multiply the progress by it. That way the threaded method does not touch UI elements at all.

like image 133
SimpleVar Avatar answered Sep 30 '22 05:09

SimpleVar


It's true that you can't know exactly how many iterations your loops will do, but you certainly can smooth your progress indicator.

Let's assume your total iterations in the worst case is xMax * yMax * worstZMax.

worstZMax = 99 because the upper bound is exclusive in Random.Next(minValue:int, maxValue:int)

Hence the total iterations in worst case scenario will be 5 * 10 * 99 = 4950.

Now we could start with that total and adjust it as required when we loose iterations after generating zMax for each y loop and we'll effectively smooth the progress calculation and reporting.

This is how I'd do it:

private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
    const int xMax = 10;
    const int yMax = 5;
    const int worstZMax = 99; //because upper is exclusive in Random.Next(lower, upper) 

    var iteration = 0;
    var total = xMax * yMax * worstZMax; //total number of iterations (worst case)

    for (var x = 1; x <= xMax; x++)
    {
        for (var y = 1; y <= yMax; y++)
        {
            var zMax = new Random().Next(50, 100);

            //get how many iterations did we miss, and adjust the total iterations
            total -= worstZMax - zMax;
            for (var z = 1; z <= zMax; z++)
            {
                iteration++;  //count this iteration
                Thread.Sleep(5); // The process

                // The progress calculation
                var progress = (double)iteration / total * pgb.Maximum;

                bgw.ReportProgress((int)progress);
            }
        }
    }
}

Hope this helps!

like image 26
Karel Tamayo Avatar answered Sep 30 '22 03:09

Karel Tamayo