如何在下面显示的第三个循环中添加进度条?
实验证据表明,在以下三个循环中,第三个是最快的。最佳性能是线程数与 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 倍,则进度似乎排队并在方法返回后继续进行。在调用该方法后将进度显示抑制一秒钟就足够了。只有当方法花费的时间太长以至于您想知道是否正在发生任何事情时,进度条才真正有用。