Возможно, вам нужна одна из более сложных перегрузок метода Parallel.For
:
public static ParallelLoopResult For<TLocal>(int fromInclusive, int toExclusive,
ParallelOptions parallelOptions, Func<TLocal> localInit,
Func<int, ParallelLoopState, TLocal, TLocal> body,
Action<TLocal> localFinally);
Выполняет a для l oop с локальными данными потока в какие итерации могут выполняться параллельно, можно настроить параметры l oop, а состояние l oop можно отслеживать и манипулировать.
Это выглядит довольно пугающе со всеми различными лямбдами надеется. Идея состоит в том, чтобы каждый поток работал с локальными данными и, наконец, объединял данные в конце. Вот как вы можете использовать этот метод для решения вашей проблемы:
double[] A = new double[1000];
double[] B = (double[])A.Clone();
object locker = new object();
var parallelOptions = new ParallelOptions()
{
MaxDegreeOfParallelism = Environment.ProcessorCount
};
Parallel.For(0, A.Length, parallelOptions,
localInit: () => new double[A.Length], // create temp array per thread
body: (i, state, temp) =>
{
double v = A[i];
temp[i] -= v;
temp[i + 1] += v / 2;
temp[i - 1] += v / 2;
return temp; // return a reference to the same temp array
}, localFinally: (localB) =>
{
// Can be called in parallel with other threads, so we need to lock
lock (locker)
{
for (int i = 0; i < localB.Length; i++)
{
B[i] += localB[i];
}
}
});
Я должен упомянуть, что рабочая нагрузка в приведенном выше примере слишком гранулированная, поэтому я не ожидаю значительного улучшения производительности от распараллеливания. Надеюсь, ваша фактическая нагрузка будет более короткой. Например, если у вас есть два вложенных цикла, распараллеливание только внешнего l oop будет работать очень хорошо, потому что внутреннее l oop обеспечит столь необходимую краткость.
Альтернативное решение: Вместо создания вспомогательных массивов для каждого потока, вы можете просто обновить непосредственно массив B и использовать блокировки только при обработке индекса в опасной зоне вблизи границ разделов :
Parallel.ForEach(Partitioner.Create(0, A.Length), parallelOptions, range =>
{
bool lockTaken = false;
try
{
for (int i = range.Item1; i < range.Item2; i++)
{
bool shouldLock = i < range.Item1 + 1 || i >= range.Item2 - 1;
if (shouldLock) Monitor.Enter(locker, ref lockTaken);
double v = A[i];
B[i] -= v;
B[i + 1] += v / 2;
B[i - 1] += v / 2;
if (shouldLock) { Monitor.Exit(locker); lockTaken = false; }
}
}
finally
{
if (lockTaken) Monitor.Exit(locker);
}
});