在 Parallel.For 中使用索引器的正确方法
Correct way to use indexer in Parallel.For
我有一个基于特定(波形)函数生成(波形)位图的方法(在下面的示例中,我只是使用 Math.Sin 来简化事情)。到目前为止,这种方法是 "serial"(无线程),但是使用的一些函数相对耗时,所以我尝试使用 Parallel.For
和 Parallel.ForEach
,但我想我使用的索引变量必须get "corrupted"(或者至少有一个不同于我预期的值)正在生成的图形将包含 "spikes" 或非邻居点之间的奇怪线。
首先是我的系列版本(有效):
Point[] points = new Point[rect.Width];
byte[] ptTypes = new byte[rect.Width];
for (int i = 0; i < rect.Width; i++)
{
double phase = MathUtil.WrapRad((MathUtil.PI2 / (rect.Width / 1d)) * i);
double value = waveform.ValueAtPhase(phase);
newPoint = new Point(rect.Left + i,
rect.Top + (int) (halfHeight + (value * -1d * halfHeight * scaleY)));
points[i] = newPoint;
if (i == 0)
ptTypes[i] = (byte)PathPointType.Start;
else
ptTypes[i] = (byte)PathPointType.Line;
}
using (GraphicsPath wavePath = new GraphicsPath(points, ptTypes))
{
gph.DrawPath(wavePen, wavePath);
}
如您所见,代码仅使用了 2 个数组(一个用于点,一个用于 PointType),因此只要将值插入到正确的元素中,这些值插入数组的顺序无关紧要数组。
下一个示例使用 Parallel.For(为了简化示例,我省略了数组的创建和实际绘制方法):
Parallel.For(0, rect.Width,
i =>
{
double phase = MathUtil.WrapRad((MathUtil.PI2 / (rect.Width / 1d)) * i);
double value = Math.Sin(phase);//waveform.ValueAtPhase(phase);
newPoint = new Point(rect.Left + i,
rect.Top + (int)(halfHeight + (value * -1d * halfHeight * scaleY)));
points[i] = newPoint;
if (i == 0)
ptTypes[i] = (byte)PathPointType.Start;
else
ptTypes[i] = (byte)PathPointType.Line;
});
最后我尝试使用带有 Parallel.ForEach 循环的分区器,但这也没有解决问题:
var rangePartitioner = Partitioner.Create(0, rect.Width);
Parallel.ForEach(rangePartitioner, (range, loopState) =>
{
for (int i = range.Item1; i < range.Item2; i++)
{
double phase = MathUtil.WrapRad((MathUtil.PI2 / (rect.Width / 1d)) * i);
double value = Math.Sin(phase);//waveform.ValueAtPhase(phase);
newPoint = new Point(rect.Left + i,
rect.Top + (int)(halfHeight + (value * -1d * halfHeight * scaleY)));
points[i] = newPoint;
if (i == 0)
ptTypes[i] = (byte)PathPointType.Start;
else
ptTypes[i] = (byte)PathPointType.Line;
}
});
佩尔
newPoint = new Point(rect.Left + i, rect.Top + (int)(halfHeight + (value * -1d * halfHeight * scaleY)));
newPoint 不在你的 for()
循环范围内——很可能线程在你到达下一行之前更新它 points[i] = newPoint;
改为var newPoint = ...
否则,您的 Parallel.For
看起来不错。
此外,这是否有不同的行为?
Math.Sin(phase);//waveform.ValueAtPhase(phase);
提供 ValueAtPhase 不会修改任何内容,您应该能够在循环中使用它。
我有一个基于特定(波形)函数生成(波形)位图的方法(在下面的示例中,我只是使用 Math.Sin 来简化事情)。到目前为止,这种方法是 "serial"(无线程),但是使用的一些函数相对耗时,所以我尝试使用 Parallel.For
和 Parallel.ForEach
,但我想我使用的索引变量必须get "corrupted"(或者至少有一个不同于我预期的值)正在生成的图形将包含 "spikes" 或非邻居点之间的奇怪线。
首先是我的系列版本(有效):
Point[] points = new Point[rect.Width];
byte[] ptTypes = new byte[rect.Width];
for (int i = 0; i < rect.Width; i++)
{
double phase = MathUtil.WrapRad((MathUtil.PI2 / (rect.Width / 1d)) * i);
double value = waveform.ValueAtPhase(phase);
newPoint = new Point(rect.Left + i,
rect.Top + (int) (halfHeight + (value * -1d * halfHeight * scaleY)));
points[i] = newPoint;
if (i == 0)
ptTypes[i] = (byte)PathPointType.Start;
else
ptTypes[i] = (byte)PathPointType.Line;
}
using (GraphicsPath wavePath = new GraphicsPath(points, ptTypes))
{
gph.DrawPath(wavePen, wavePath);
}
如您所见,代码仅使用了 2 个数组(一个用于点,一个用于 PointType),因此只要将值插入到正确的元素中,这些值插入数组的顺序无关紧要数组。
下一个示例使用 Parallel.For(为了简化示例,我省略了数组的创建和实际绘制方法):
Parallel.For(0, rect.Width,
i =>
{
double phase = MathUtil.WrapRad((MathUtil.PI2 / (rect.Width / 1d)) * i);
double value = Math.Sin(phase);//waveform.ValueAtPhase(phase);
newPoint = new Point(rect.Left + i,
rect.Top + (int)(halfHeight + (value * -1d * halfHeight * scaleY)));
points[i] = newPoint;
if (i == 0)
ptTypes[i] = (byte)PathPointType.Start;
else
ptTypes[i] = (byte)PathPointType.Line;
});
最后我尝试使用带有 Parallel.ForEach 循环的分区器,但这也没有解决问题:
var rangePartitioner = Partitioner.Create(0, rect.Width);
Parallel.ForEach(rangePartitioner, (range, loopState) =>
{
for (int i = range.Item1; i < range.Item2; i++)
{
double phase = MathUtil.WrapRad((MathUtil.PI2 / (rect.Width / 1d)) * i);
double value = Math.Sin(phase);//waveform.ValueAtPhase(phase);
newPoint = new Point(rect.Left + i,
rect.Top + (int)(halfHeight + (value * -1d * halfHeight * scaleY)));
points[i] = newPoint;
if (i == 0)
ptTypes[i] = (byte)PathPointType.Start;
else
ptTypes[i] = (byte)PathPointType.Line;
}
});
佩尔
newPoint = new Point(rect.Left + i, rect.Top + (int)(halfHeight + (value * -1d * halfHeight * scaleY)));
newPoint 不在你的 for()
循环范围内——很可能线程在你到达下一行之前更新它 points[i] = newPoint;
改为var newPoint = ...
否则,您的 Parallel.For
看起来不错。
此外,这是否有不同的行为?
Math.Sin(phase);//waveform.ValueAtPhase(phase);
提供 ValueAtPhase 不会修改任何内容,您应该能够在循环中使用它。