在Parallel.For中使用索引器的正确方法

时间:2015-07-13 12:24:50

标签: c# arrays foreach parallel-processing

我有一种方法可以根据特定的(波形)函数生成(波形)位图(在下面的例子中,我只是简单地使用Math.Sin来简化)。到目前为止,这个方法是“串行”(没有线程)但是使用的一些函数相对耗时,所以我尝试使用Parallel.ForParallel.ForEach,但我想我使用的索引变量必须得到“损坏的“(或至少有另一个值超出我的预期)生成的图形将包含”尖峰“或不是邻居的点之间的奇怪线。

首先是我的序列版本(可行):

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个数组(一个用于Points,一个用于PointTypes),因此这些值插入数组的顺序无关紧要,只要将值插入到数组的正确元素中即可

下一个例子是使用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;
    }
});

佩尔

1 个答案:

答案 0 :(得分:2)

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不会修改任何内容,您应该能够在循环中使用它。