2016-08-12 2 views
1

Я никогда не использовал Parallel.ForEach, но я играл с ним и нашел это явление.Параллельный ForEach Предоставление разных результатов в разных оккасиях

Я бег параллельного цикла (код найти на MSDN https://msdn.microsoft.com/en-us/library/dd997393(v=vs.110).aspx сделал редактировать его с subtotal *=2, чтобы попытаться понять, что он делает) с перечислимым диапазоном, во-первыми (0,1), а затем (0,1,2) потом запустить второй раз, но после сна нити в течение 200 миллисекунд, а затем результат отличается

если Thread.sleep(200) не закомментирован это результаты

result 1 = 2 
result 2 = 6 
result 3 = 4 

если Thread.sleep(200) закомментирован это результаты

result 1 = 2 
result 2 = 6 
result 3 = 6 

Вот код

Stopwatch timer = new Stopwatch(); 
int[] nums = Enumerable.Range(0, 1).ToArray(); 
long total = 0; 
for (int i = 0; i < 2; i++) 
{ 
    timer.Restart(); 
    total = 0; 
    if (i == 0) nums = Enumerable.Range(0, 1).ToArray(); 
    if (i == 1) nums = Enumerable.Range(0, 2).ToArray(); 
    Parallel.ForEach<int, long>(nums,() => 0,(j, loop, subtotal) => 
    { 
     subtotal += 1; 
     subtotal *= 2; 
     return subtotal; 
    },(finalResult) => Interlocked.Add(ref total, finalResult)); 

    Console.WriteLine("The total from Parallel.ForEach is {0:N0} and took {1}", total, timer.Elapsed); 
    timer.Stop(); 
    //Thread.Sleep(200); 
} 

timer.Restart(); 
nums = Enumerable.Range(0, 2).ToArray(); 
total = 0; 
Parallel.ForEach<int, long>(nums,() => 0, (j, loop, subtotal) => 
{ 
    subtotal += 1; 
    subtotal *= 2; 
    return subtotal; 
},(finalResult) => Interlocked.Add(ref total, finalResult)); 

Console.WriteLine("The total from Parallel.ForEach is {0:N0} and took {1}", total, timer.Elapsed); 
timer.Stop(); 

я понял, это надо делать с нитями, работающих друг над другом, но это, кажется, как ошибка

Примечание Я сделал взглянуть на Simulation gives different result with normal for loop Vs Parallel For
Почему это происходит?

+1

@ JánЯabčan Можете ли вы подать se разработать. Я не понимаю, что делает этот код, когда он умножает 2 и почему? – Neil

+0

Я даже не понимаю, как вы получаете свои результаты ... – sam

+0

@sam измените ваш thread.sleep на 1000 и посмотрите, получите ли вы его – Neil

ответ

3

Поскольку этот код плохо определен:

Parallel.ForEach<int, long>(nums,() => 0,(j, loop, subtotal) => 
{ 
    subtotal += 1; 
    subtotal *= 2; 
    return subtotal; 
},(finalResult) => Interlocked.Add(ref total, finalResult)); 

В том, что если одного нить выполняет обе итерации, то вы получите результат 6. Эффективно, вы делаете:

subTotal = 0; //From init 
subTotal += 1; //=1 First iteration 
subTotal *= 2; //=2 First iteration 
subTotal += 1; //=3 Second iteration 
subTotal *= 2; //=6 Second iteration 
total += subTotal; //=6 End gathering (actually interlocked) 

Но если две темы делят работу, вы получаете

subTotal1 = 0; //From init 
subTotal2 = 0; //From init 
subTotal2 += 1; //=1 
subTotal1 += 1; //=1 
subTotal1 *= 2; //=2 
subTotal2 *= 2; //=2 
total += subTotal1 //=2 End gathering 1 (interlocked) 
total += subTotal2 //=4 End gathering 2 (interlocked) 
+0

Спасибо за объяснение. поэтому просто для того, чтобы убедиться, что это правильно, параллельный foreach никогда не должен использоваться для вычислений, тогда он используется там, где каждая итерация имеет свое собственное исполнение не для значений, которые отправляются на следующую итерацию? – Neil

+1

@Neil - Я не знаю, каков ваш вопрос. Но если вы используете вариант «Parallel.ForEach», который использует локальное состояние, функцию, которая мутирует локальное состояние и окончательное действие, которое собирает все результаты и объединяет их, имеет смысл сделать это, если вы получите последовательные результаты независимо от того, сколько потоков работает (так, сколько копий локального состояния будет существовать) и как результаты будут объединены в конечном действии. У вас был расчет, который не работает так - это важно для того, сколько потоков используется, и, следовательно, сколько копий локального состояния существует. –

+0

Вот что я просил :). благодарю вас за ваш ответ и разъяснения. проголосовавший и принятый – Neil

Смежные вопросы