如何根据嵌套循环中的多个变量报告进度(对于进度条)?

Posted

技术标签:

【中文标题】如何根据嵌套循环中的多个变量报告进度(对于进度条)?【英文标题】:How can I report progress based on multiple variables in a nested loop (for a progress bar)? 【发布时间】:2017-04-29 22:28:48 【问题描述】:

我有一个 BackgroundWorker 和一个 ProgressBar。工作时,BackgroundWorker 会运行三重 for 循环并向 ProgressBar 报告进度。

目前,报告的进度仅是最外层循环 (xProgress) 的进度,该循环有效,但运行不顺畅。目标是让 ProgressBar 也考虑内部循环的进度百分比,以便 ProgressBar 更新更顺畅、更准确。

DoWork 方法:

    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);
                
            
        
    

【问题讨论】:

y-loop 为 100% 时,您即将将 x 增加 1,因此您可以说 100% y1% x,对于 z 和 y 也是如此。所以var prog = xProg + yProg*0.01d*xMax + zProg*0.01d*yMax*0.01d*xMax(或类似的东西)。您可能必须让循环从 0 到 max-1 而不是 1 到 max 才能正确计算,但我不确定,您应该检查一下。 很遗憾,将progressCalc 设置为该公式会导致最终结果超过1.00d,从而导致ProgressBar 溢出。 我确实告诉过你应该检查一下。你是从 0 到 max-1 吗? 更改边界后,ProgressBar 仍然会溢出该公式。 是的,我太傻了。正确方法见答案 【参考方案1】:

即使您事先不知道自己的最佳/最坏情况,这也有效。 任何最大值都可以以与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);

顺便说一句,我会将pgb.Maximum 替换为1 并在OnProgressHandler 中将进度乘以它。这样线程方法根本不会触及 UI 元素。

【讨论】:

【参考方案2】:

确实,您无法确切知道循环将执行多少次迭代,但您当然可以平滑进度指示器。

让我们假设您在最坏情况下的总迭代次数为 xMax * yMax * badZMax。

worstZMax = 99,因为上限在 Random.Next(minValue:int, maxValue:int) 中是独占的

因此,最坏情况下的总迭代次数将是5 * 10 * 99 = 4950

现在我们可以从这个总数开始,并在为每个 y 循环生成 zMax 后松散迭代时根据需要调整它,我们将有效地平滑进度计算和报告。

这就是我的做法:

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);
            
        
    

希望这会有所帮助!

【讨论】:

【参考方案3】:

更新答案:

由于内部循环的迭代次数未知,您可以决定它应该前进多少步。例如 10。

然后你可以这样做:

progressbarMaximum = xMax * yMax * 10

在 z-loop 之前,您可以通过将 zMax 除以 10 来获得 zModulo。 在你的 z 循环中检查是否

zMax % zModulo == 0

然后增加进度条。

旧答案:

如果将 xMax、yMax 和 zMax 相乘,则得到最内层循环的总迭代次数。

将进度条最大值设置为该值。

在内循环递增进度条的每次迭代中。

【讨论】:

不幸的是,如代码所示,每个 y 的 zMax 都会发生变化。所以,我不知道迭代的总数。【参考方案4】:

不能准确地说。这样想——如果第一个随机数都是 50 并且你已经完成了 2 个外部循环的一半——如果剩下的随机数是 50——你已经完成了 50%。如果其余的随机数为 100 - 你将只完成 33%。所以你不知道进度条应该有多远。

(当然随机结果通常不会是这样的。这只是为了说明这一点。)

【讨论】:

以上是关于如何根据嵌套循环中的多个变量报告进度(对于进度条)?的主要内容,如果未能解决你的问题,请参考以下文章

后台线程中的进度条.setProgress(variable)

使用一个进度条java / Android下载多个文件

如何获取块中的 JSON 数据以报告进度? [复制]

python--并行计算

如何在 WPF 中制作选取框进度条?

python实例文本进度条