Цикл Parallel-For дает другие результаты суммы, чем одиночный цикл for. Что мне не хватает?

У нас есть непонятная ошибка в одной из наших программ. Я сузил, по крайней мере, часть его до этого параллельного цикла.

i — это int[], в целях тестирования заполненный целыми числами от 0 до 99999. runningTotal — это тип long. lockObject — это новый объект();

Кажется, что цикл Parallel-For всегда возвращает общее количество 704 982 704 после завершения; однопоточный цикл возвращает 4 999 950 000.

Так что, очевидно, проблема с потоками, но я просто не вижу ошибки. Правильно ли реализован цикл Parallel-For?

Соответствующий код ниже:

//i int[] test ---------------------------------------------
object lockObject = new object();
int[] i = new int[100000];
for (int x = 0; x < i.Length; x++)
{
    i[x] = x;
}
long runningTotal = 0;

Parallel.For(0, i.Length,
() => 0,
(x, loopState, subtotal) =>
{
    subtotal += i[x];
    return subtotal;
},
(s) =>
{
    lock (lockObject)
    {
        runningTotal += s;
    }
}
);
runningTotal = 0;
for (int x = 0; x < i.Length; x++)
{
    runningTotal += i[x];
}

person Free Coder 24    schedule 13.01.2016    source источник
comment
Это действительно странно. В моих тестах он периодически переполняется, когда localinit возвращает 0 (Int32), но работает надежно, если localinit равно Int64 (Parallel.For(0, i.Length, () => 0L ...)   -  person Kirill Shlenskiy    schedule 13.01.2016
comment
Я попробовал то, что вы предложили, и да, теперь он работает отлично и, похоже, решил нашу проблему. Спасибо! Если вы можете поместить это в ответ, я отмечу это как ответ.   -  person Free Coder 24    schedule 13.01.2016
comment
На самом деле это не тот ответ, которым я могу быть доволен, пока я точно не узнаю, почему возникает проблема. Я немного покопаюсь и доложу, как только получу исчерпывающее объяснение.   -  person Kirill Shlenskiy    schedule 13.01.2016
comment
Я думаю я знаю, что это такое. Количество параллельных рабочих   -  person Kirill Shlenskiy    schedule 13.01.2016
comment
Возможно, инициализация должна быть помечена как длинная, потому что, несмотря на то, что она находится в параллельном цикле, может быть выделена только 1 задача, что приведет к переполнению. Степень параллелизма автоматически регулируется базовыми компонентами системы. Вы к этому склоняетесь?   -  person Free Coder 24    schedule 13.01.2016
comment
Да, это точно.   -  person Kirill Shlenskiy    schedule 13.01.2016
comment
Проголосовал за предоставление точного кода, иллюстрирующего проблему.   -  person    schedule 13.01.2016


Ответы (1)


Основная причина в том, что локальное состояние объявляется как int и иногда переполняется.

Это происходит время от времени и связано с количеством рабочих потоков, которые Parallel.For запускает для текущей задачи. Вы можете добиться надежного переполнения, если ограничите MaxDegreeOfParallelism значением 1.

Исправление состоит в том, чтобы объявить локальное состояние как long:

Parallel.For(0, i.Length, () => 0L, ...)

... тогда переполнение никогда не происходит. В остальном с вашей логикой все в порядке.

person Kirill Shlenskiy    schedule 13.01.2016
comment
Помечено как ответ, так как мы поняли это. Спасибо за вашу помощь и понимание! - person Free Coder 24; 13.01.2016
comment
@FreeCoder24, это было весело. Спасибо за краткое описание проблемы. - person Kirill Shlenskiy; 13.01.2016