Я просто заглядываю к новой.NET 4,0 функции. С этим я делаю попытку простого использования вычисления Parallel.For
и нормальное for(x;x;x)
цикл.
Однако я получаю различные результаты приблизительно 50% времени.
long sum = 0;
Parallel.For(1, 10000, y =>
{
sum += y;
}
);
Console.WriteLine(sum.ToString());
sum = 0;
for (int y = 1; y < 10000; y++)
{
sum += y;
}
Console.WriteLine(sum.ToString());
Мое предположение - то, что потоки пытаются обновить "сумму" одновременно.
Существует ли очевидный путь вокруг этого?
Вы не можете этого сделать. sum
разделяется между вашими параллельными потоками. Вам нужно убедиться, что к переменной sum
одновременно обращается только один поток:
// DON'T DO THIS!
Parallel.For(0, data.Count, i =>
{
Interlocked.Add(ref sum, data[i]);
});
НО... Это антипаттерн, потому что вы эффективно сериализовали цикл, поскольку каждый поток будет блокировать Interlocked.Add
.
Что вам нужно сделать, так это добавить промежуточные итоги и объединить их в конце следующим образом:
Parallel.For<int>(0, result.Count, () => 0, (i, loop, subtotal) =>
{
subtotal += result[i];
return subtotal;
},
(x) => Interlocked.Add(ref sum, x)
);
Вы можете найти дальнейшее обсуждение этого на MSDN: http://msdn.microsoft.com/en-us/library/dd460703.aspx
PLUG: Вы можете найти больше об этом в главе 2 на A Guide to Parallel Programming
Следующее также определенно стоит прочитать...
sum + = y;
фактически равно sum = sum + y;
. Вы получаете неверные результаты из-за следующего состояния гонки:
sum
sum
sum + y1
и сохраняет результат в sum
sum + y2
и сохраняет результат в sum
sum
теперь равно sum + y2
вместо из сумма + y1 + y2
.
Ваше предположение верно.
Когда вы пишете sum += y
, среда выполнения делает следующее:
y
в стекЕсли два потока читают поле одновременно, изменение, сделанное первым потоком, будет перезаписано вторым потоком.
Вам необходимо использовать Interlocked.Add
, который выполняет сложение как одну атомарную операцию.
Я думаю, важно отметить, что этот цикл не может быть разделен для параллелизма, потому что, как уже упоминалось выше, каждая итерация цикла зависит от приора. Параллельный для предназначен для явно параллельных задач, таких как масштабирование пикселей и т. Д., Потому что каждая итерация цикла не может иметь зависимости данных вне своей итерации.
Parallel.For(0, input.length, x =>
{
output[x] = input[x] * scalingFactor;
});
Выше приведен пример кода, который позволяет легко разделить для параллелизма. Однако предупреждаем, что за параллелизм приходится платить, даже цикл, который я использовал в качестве примера выше, слишком прост, чтобы возиться с параллелизмом, потому что время настройки занимает больше времени, чем время, сэкономленное благодаря параллелизму.
Увеличение значения long не является атомарной операцией.
если в этом коде два параметра. Например
long sum1 = 0;
long sum2 = 0;
Parallel.For(1, 10000, y =>
{
sum1 += y;
sum2=sum1*y;
}
);
что мы будем делать? Я предполагаю, что нужно использовать массив!