Interlocked.Increment () работает не так, как я ожидаю в параллельной библиотеке задач - PullRequest
6 голосов
/ 23 января 2011

У меня есть цикл Parallel.ForEach (), который берет список URL-адресов и загружает каждый из них для некоторой дополнительной обработки.Вне моего цикла я объявил переменную счетчика цикла, а внутри тела цикла я использую Interlocked.Increment (), думая, что это будет лучшим способом сохранить «потокобезопасный» способ увеличения числа при выполнении каждого взаимодействия цикла.

int counter = 0;

Parallel.ForEach(urlList, (url, state) =>
{
    // various code statments

    Interlocked.Increment( ref counter );

    Debug.WriteLine(" ......... counter: " + counter);
});

Я бы подумал, что увижу что-то похожее на:

 ......... 1
 ......... 2
 ......... 3
 ......... 4
 ......... 5
 ......... 
 ......... 
 ......... n

Но вместо этого я получаю 16 "......... 0" (этопотому что у меня есть двухъядерный компьютер с 8 собственными ядрами, но включена поддержка многопоточности, что дает мне всего 16 ядер).Затем я начну видеть, что счетчик по большей части нормально увеличивается, но иногда я увижу дублированные или даже тройные значения счетчика в выходных данных отладки.

Использование Parallel.ForEach (), что является лучшим способомсчитать итерации цикла?Спасибо за любой совет.

Ответы [ 3 ]

14 голосов
/ 23 января 2011

Interlocked.Increment вернет ваше увеличенное значение.

Итак,

int counter = 0;

Parallel.ForEach(urlList, (url, state) =>
{
    // various code statments

    var counterNow = Interloced.Increment( ref counter );

    Debug.WriteLine(" ......... counter: " + counterNow);
});

должен возвращать значение счетчика при увеличении.

Отредактировано через некоторое время:

Для пояснения:

Если вы работаете на многопроцессорных / многоядерных компьютерах и имеете несколько потоков приложений, вам нужно знать, что значения переменных, которые вы видите, могут не отражать фактическое текущее состояние этой переменной. Это связано с тем, что может быть много отдельных кэшей для каждого процессора, матрицы или сокета, и ваш поток может читать кэшированное значение (сохраняя попадание для чтения основной памяти)

Если вы просто хотите прочитать значение, которое обновляется несколькими потоками, вы должны использовать Interlocked.Read(), чтобы гарантировать, что у вас есть текущее значение для counter.

7 голосов
/ 23 января 2011

Это потому, что Increment + WriteLine вместе не являются атомарными.
Это может быть то, что thread1 увеличивает counter, затем thread2 увеличивает его снова, и затем два потока достигают части WriteLine с одинаковым значением counter.

1 голос
/ 23 января 2011

Я не уверен, но я думаю, что это, вероятно, связано с тем, как захват переменных работает в лямбдах Вы пытались поместить его в отдельную функцию или переместить переменную наружу и объявить ее статической?

...