如何向下面所示的第三个循环添加进度条?
实验数据表明,以下三个循环中第三个是最快的。当线程数与CPU的逻辑处理器数量相同时,性能最佳。我认为这是由于减少了分配和释放线程资源的时间。
这是用于生成噪声地图的代码。有时需要进度条来显示处理时间。
for (int j = 0; j < data.Length; j++)
{
var x = (location.X + (j % width));
var y = (location.Y + (j / width));
Vector3 p = new Vector3(x, y, frame);
p *= zoom;
float val = noise.GetNoise(p);
data[j] += val;
min = Math.Min(min, val);
max = Math.Max(max, val);
}
Parallel.For(0, data.Length, (i) => {
var x = (location.X + (i % width));
var y = (location.Y + (i / width));
Vector3 p = new Vector3(x, y, frame);
p *= zoom;
float val = noise.GetNoise(p);
data[i] += val;
min = Math.Min(min, val);
max = Math.Max(max, val);
});
Parallel.For(0, threads, (i) =>
{
int from = i * data.Length / threads;
int to = from + data.Length / threads;
if (i == threads - 1) to = data.Length - 1;
for (int j = from; j < to; j++)
{
var x = (location.X + (j % width));
var y = (location.Y + (j / width));
Vector3 p = new Vector3(x, y, frame);
p *= zoom;
float val = noise.GetNoise(p);
data[j] += val;
min = Math.Min(min, val);
max = Math.Max(max, val);
}
}
);
为了避免浪费时间频繁绘制进度条,最好是将其更新速率限制在每秒几次。
通过添加 IProgress,我已经到达这里,它几乎可以工作。问题在于进度条在 parallel.for 完成后才更新。
private async Task<int> FillDataParallelAsync(int threads, IProgress<int> progress)
{
int precent = 0;
/// parallel loop - easy and fast.
Parallel.For(0, threads, (i) =>
{
int from = i * data.Length / threads;
int to = from + data.Length / threads;
if (i == threads - 1) to = data.Length - 1;
for (int j = from; j < to; j++)
{
var x = (location.X + (j % width));
var y = (location.Y + (j / width));
Vector3 p = new Vector3(x, y, frame);
p *= zoom;
float val = noise.GetNoise(p);
data[j] += val;
min = Math.Min(min, val);
max = Math.Max(max, val);
if(j%(data.Length / 100) ==0)
{
if (progress != null)
{
progress.Report(precent);
}
Interlocked.Increment(ref precent);
}
}
}
);
return 0;
}
经过长时间的努力,现在看起来是这样的。
private Boolean FillDataParallel3D(int threads, CancellationToken token, IProgress<int> progress)
{
int precent = 0;
Vector3 imageCenter = location;
imageCenter.X -= width / 2;
imageCenter.Y -= height / 2;
ParallelOptions options = new ParallelOptions { CancellationToken = token };
/// parallel loop - easy and fast.
try
{
ParallelLoopResult result =
Parallel.For(0, threads, options, (i, loopState) =>
{
int from = i * data.Length / threads;
int to = from + data.Length / threads;
if (i == threads - 1) to = data.Length - 1;
for (int j = from; j < to; j++)
{
if (loopState.ShouldExitCurrentIteration) break;
Vector3 p = imageCenter;
p.X += (j % width);
p.Y += (j / width);
p *= zoom;
float val = noise.GetNoise(p);
data[j] += val;
min = Math.Min(min, val);
max = Math.Max(max, val);
if (j % (data.Length / 100) == 0)
{
try { if (progress != null) progress.Report(precent); }
catch { }
Interlocked.Increment(ref precent);
}
}
}
);
return result.IsCompleted;
}
catch { }
return false;
}
进度条通过每个线程逐步增加,最终总共增加100次。更新进度条仍然存在一些延迟,但这似乎是不可避免的。例如,如果进度条在少于绘制100个更新的时间内增加了100次,进度似乎会排队,并在方法返回后继续进行。调用该方法后抑制显示进度1秒钟足以解决问题。当方法执行时间很长时,进度条才真正有用,否则您可能会想知道是否正在执行任何操作。
完整项目请访问https://github.com/David-Marsh/Designer。