如何根据嵌套循环中的多个变量报告进度(对于进度条)?
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% y
是 1% 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%。所以你不知道进度条应该有多远。
(当然随机结果通常不会是这样的。这只是为了说明这一点。)
【讨论】:
以上是关于如何根据嵌套循环中的多个变量报告进度(对于进度条)?的主要内容,如果未能解决你的问题,请参考以下文章