Циклы Parallel.ForEach и foreach для коллекции BlockingCollection.GetConsumingEnumerable () в TaskFactory.Tasks - PullRequest
3 голосов
/ 17 апреля 2011

Я экспериментировал с обоими этими циклами и заметил, что хотя обычный цикл foreach в делегате Action Task должен работать параллельно, он не обрабатывает элементы параллельно. Однако, если я заменю его на Parallel.ForEach, я увижу, что данные обрабатываются параллельно в нескольких потоках.

Код 1:

Task loadingTask1 = Factory.StartNew(() =>
        {
            foreach (MyOneClass dg in Queue.GetConsumingEnumerable())
            {

                MyOtherClass vl = new MyOtherClass();
                vl.Id = dg.Id;
                vl.PerformTimeConsumingAction();

                OutputQueue.Add(vl);
            }
        });

Код 2:

Task loadingTask2 = Factory.StartNew(() =>
        {
            Parallel.ForEach(Queue.GetConsumingEnumerable(), (dg) =>
            {

                MyOtherClass vl = new MyOtherClass();
                vl.Id = dg.Id;
                vl.PerformTimeConsumingAction();

                OutputQueue.Add(vl);
            });
        });

Код 1 при запуске с оператором Console.Write на каждой итерации, кажется, ожидает завершения предыдущего цикла, пока не перехватит следующий, но Код 2 обрабатывает несколько элементов параллельно.

Не правильно ли я понимаю обычный foreach в Task.Action? Я думал, что .NET запустит столько задач для задачи, сколько и нагрузки, и каждая итерация foreach будет обрабатываться параллельно.

Я также пытался передать результат PLINQ обоим вышеприведенным кодам и тому же поведению наблюдателя: обычный foreach, казалось, ожидал завершения предыдущей итерации, чтобы начать следующую, хотя я использовал .AsParallel() и .WithExecutionMode(ParallelExecutionMode.ForceParallelism) директивы.

Любое понимание будет высоко оценено. Мне известен класс OrderingPartitioner, и я могу попробовать использовать его

1 Ответ

18 голосов
/ 17 апреля 2011

Обычный foreach всегда выполняет свои итерации последовательно. В некоторых ситуациях нет волшебства, которое превращает его в параллельную конструкцию. Это все равно, что бросить вас в яму отчаяния , потому что тогда было бы трудно утверждать правильность чего-то такого простого, как цикл foreach. К счастью, одна из целей C # - бросить вас в пропасть успеха:

C# throwing you into the pit of success

Если вы помещаете цикл foreach в отдельное задание, все итерации выполняются последовательно, но вы можете выполнять другой код параллельно со всем foreach.

Поток выполнения регулярного foreach для отдельной задачи выглядит следующим образом:

              |
            __v_
           /    \
other code |    | foreach iteration 1
other code |    | foreach iteration 2
other code |    | foreach iteration 3
           ......
other code |    | foreach iteration n-1
other code |    | foreach iteration n
           v    v

И поток выполнения Parallel.Foreach выглядит так:

                  |
 _________________v________________
/    /    /    /    \    \    \    \
|1   |2   |3   |....|    |n-2 |n-1 |n
\____\____\____\____/____/____/____/
                  |
                  v

Надеюсь, это поможет понять, что происходит.

...