C# Parallel.ForEach throw IndexOutOfBounds but foreach does not?

95 Views Asked by At

My program must check the state of all elements in array. When I use foreach it's ok, but in Parallel.ForEach and Parallel.For it throws index out of bounds exception.

This code works:

foreach (var p in _currentSpherePoints)
{
    point = p + center;
    point.X = (point.X + _field.GetLength((int)VectorInt.Dimension.X)) % _field.GetLength((int)VectorInt.Dimension.X);
    point.Y = (point.Y + _field.GetLength((int)VectorInt.Dimension.Y)) % _field.GetLength((int)VectorInt.Dimension.Y);
    point.Z = (point.Z + _field.GetLength((int)VectorInt.Dimension.Z)) % _field.GetLength((int)VectorInt.Dimension.Z);
    if (_field[point.X, point.Y, point.Z] != 0)
    {
        isCrossed = true;
        break;
    }
}

And this not:

Parallel.ForEach(_currentSpherePoints, (p, state) =>
{
    point = p + center;
    point.X = (point.X + _field.GetLength((int)VectorInt.Dimension.X)) % _field.GetLength((int)VectorInt.Dimension.X);
    point.Y = (point.Y + _field.GetLength((int)VectorInt.Dimension.Y)) % _field.GetLength((int)VectorInt.Dimension.Y);
    point.Z = (point.Z + _field.GetLength((int)VectorInt.Dimension.Z)) % _field.GetLength((int)VectorInt.Dimension.Z);
    if (_field[point.X, point.Y, point.Z] != 0)
    {
        isCrossed = true;
        state.Stop();
    }
});
public bool IsSphereCrossed(VectorInt center)
{
    bool isCrossed = false;
    var point = new VectorInt(0, 0, 0);

    Parallel.ForEach(_currentSpherePoints, (p, state) =>
    {
        point = p + center;
        point.X = (point.X + _field.GetLength((int)VectorInt.Dimension.X)) % _field.GetLength((int)VectorInt.Dimension.X);
        point.Y = (point.Y + _field.GetLength((int)VectorInt.Dimension.Y)) % _field.GetLength((int)VectorInt.Dimension.Y);
        point.Z = (point.Z + _field.GetLength((int)VectorInt.Dimension.Z)) % _field.GetLength((int)VectorInt.Dimension.Z);
        if (_field[point.X, point.Y, point.Z] != 0)
        {
            isCrossed = true;
            state.Stop();
        }
    });
    return isCrossed;
}
  

As you see, I don't change collection, so I don't know, where there is a problem. _currentSpherePoints contains my own Vector class.

1

There are 1 best solutions below

0
On BEST ANSWER

Now that you have shared the whole function, here's the problem:

    public bool IsSphereCrossed(VectorInt center)
    {
        bool isCrossed = false;
        var point = new VectorInt(0, 0, 0); // This variable is changed randomly

        Parallel.ForEach(_currentSpherePoints, (p, state) =>
        {
            point = p + center;
            point.X = (point.X + _field.GetLength((int)VectorInt.Dimension.X)) % _field.GetLength((int)VectorInt.Dimension.X);
            point.Y = (point.Y + _field.GetLength((int)VectorInt.Dimension.Y)) % _field.GetLength((int)VectorInt.Dimension.Y);
            point.Z = (point.Z + _field.GetLength((int)VectorInt.Dimension.Z)) % _field.GetLength((int)VectorInt.Dimension.Z);
            if (_field[point.X, point.Y, point.Z] != 0)
            {
                isCrossed = true;
                state.Stop();
            }
        });
        return isCrossed;
    }

Notice point variable is shared between all runs of Parallel.ForEach, which you can't be sure to be the same for each whole run. For example, in one thread, point may be assigned a value, and then the context switch to run with another p and point is reassigned to another value and when the thread for previous p is back, point no longer has the correct value.

You need to ensure it's immutable, probably by declaring it inside Parallel.ForEach.